From 8c6022b78ae1e376ce0db95e1d49738e8b1ee2dd Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 16 Mar 2022 22:37:34 +0100 Subject: [PATCH 001/505] QL: add query detecting inconsistent deprecations --- .../queries/bugs/InconsistentDeprecation.ql | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 ql/ql/src/queries/bugs/InconsistentDeprecation.ql diff --git a/ql/ql/src/queries/bugs/InconsistentDeprecation.ql b/ql/ql/src/queries/bugs/InconsistentDeprecation.ql new file mode 100644 index 00000000000..366d2daa4f5 --- /dev/null +++ b/ql/ql/src/queries/bugs/InconsistentDeprecation.ql @@ -0,0 +1,24 @@ +/** + * @name Inconsistent deprecation + * @description A deprecated predicate that overrides a non-deprecated predicate is an indication that the super-predicate should be deprecated. + * @kind problem + * @problem.severity warning + * @id ql/inconsistent-deprecation + * @tags correctness + * maintanability + * @precision very-high + */ + +import ql + +predicate overrides(ClassPredicate sub, ClassPredicate sup, string description, string overrides) { + sub.overrides(sup) and description = "predicate" and overrides = "predicate" +} + +from AstNode sub, AstNode sup, string description, string overrides +where + overrides(sub, sup, description, overrides) and + sub.hasAnnotation("deprecated") and + not sup.hasAnnotation("deprecated") +select sub, "This deprecated " + description + " overrides $@. Consider deprecating both.", sup, + "a non-deprecated " + overrides From af112a011a031a4d7be4cb3a30e5f62730e2409f Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Sat, 18 Dec 2021 14:35:38 +0100 Subject: [PATCH 002/505] QL: Add query detecting suspiciously missing parameters from the QLDoc of a predicate --- .../queries/style/MissingParameterInQlDoc.ql | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 ql/ql/src/queries/style/MissingParameterInQlDoc.ql diff --git a/ql/ql/src/queries/style/MissingParameterInQlDoc.ql b/ql/ql/src/queries/style/MissingParameterInQlDoc.ql new file mode 100644 index 00000000000..1fe962154f8 --- /dev/null +++ b/ql/ql/src/queries/style/MissingParameterInQlDoc.ql @@ -0,0 +1,83 @@ +/** + * @name Missing QLDoc for parameter + * @description It is suspicious when a predicate has a parameter that is + * unmentioned in the qldoc, and the qldoc contains a + * code-fragment mentioning a non-parameter. + * @kind problem + * @problem.severity warning + * @precision high + * @id ql/missing-parameter-qldoc + * @tags maintainability + */ + +import ql + +/** + * Gets a fragment enclosed in backticks (`) from a QLDoc of the predicate `p`. + * Skips code-blocks that are triple-quoted. + */ +private string getACodeFragment(Predicate p) { + result = p.getQLDoc().getContents().regexpFind("`([^`]*)`(?!`)", _, _) +} + +/** Gets a parameter name from `p`. */ +private string getAParameterName(Predicate p) { result = p.getParameter(_).getName() } + +/** + * Gets the name of a parameter of `p` that is mentioned in any way in the QLDoc of `p`. + * Also includes names that are mentioned in non-code fragments. + */ +private string getADocumentedParameter(Predicate p) { + result = p.getQLDoc().getContents().regexpFind("\\b\\w[\\w_]*\\b", _, _) and + result.toLowerCase() = getAParameterName(p).toLowerCase() +} + +/** + * Get something that looks like a parameter name from the QLDoc, + * but which is not a parameter of `p`. + */ +private string getAMentionedNonParameter(Predicate p) { + exists(string fragment | fragment = getACodeFragment(p) | + result = fragment.substring(1, fragment.length() - 1) + ) and + result.regexpMatch("^[a-z]\\w+$") and + not result.toLowerCase() = getAParameterName(p).toLowerCase() and + not result = ["true", "false", "NaN"] and // keywords + not result.regexpMatch("\\d+") and // numbers + // predicates get mentioned all the time, it's fine. + not result = + any(Predicate pred | pred.getLocation().getFile() = p.getLocation().getFile()).getName() and + // classes get mentioned all the time, it's fine. + not result = + any(TypeExpr t | t.getLocation().getFile() = p.getLocation().getFile()) + .getResolvedType() + .getName() +} + +/** Gets a parameter name from `p` that is not mentioned in the qldoc. */ +private string getAnUndocumentedParameter(Predicate p) { + result = getAParameterName(p) and + not result.toLowerCase() = getADocumentedParameter(p).toLowerCase() and + not result = ["config", "conf", "cfg"] // DataFlow configurations are often undocumented, and that's fine. +} + +/** Holds if `p` has documented parameters, but `param` is undocumented */ +private predicate missingDocumentation(Predicate p, string param) { + param = getAnUndocumentedParameter(p) and + exists(getADocumentedParameter(p)) +} + +/** Gets the one string containing the undocumented parameters from `p` */ +private string getUndocumentedParameters(Predicate p) { + result = strictconcat(string param | missingDocumentation(p, param) | param, ", or ") +} + +/** Gets the parameter-like names mentioned in the QLDoc of `p` that are not parameters. */ +private string getMentionedNonParameters(Predicate p) { + result = strictconcat(string param | param = getAMentionedNonParameter(p) | param, ", and ") +} + +from Predicate p +select p, + "The QLDoc has no documentation for " + getUndocumentedParameters(p) + ", but the QLDoc mentions " + + getMentionedNonParameters(p) From ecd3aceb078c15135e516b9856fde261251fae85 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 20 Dec 2021 09:43:22 +0100 Subject: [PATCH 003/505] QL: add test for ql/missing-parameter-qldoc --- .../queries/style/MissingParameterInQlDoc/Foo.qll | 14 ++++++++++++++ .../MissingParameterInQlDoc.expected | 2 ++ .../MissingParameterInQlDoc.qlref | 1 + 3 files changed, 17 insertions(+) create mode 100644 ql/ql/test/queries/style/MissingParameterInQlDoc/Foo.qll create mode 100644 ql/ql/test/queries/style/MissingParameterInQlDoc/MissingParameterInQlDoc.expected create mode 100644 ql/ql/test/queries/style/MissingParameterInQlDoc/MissingParameterInQlDoc.qlref diff --git a/ql/ql/test/queries/style/MissingParameterInQlDoc/Foo.qll b/ql/ql/test/queries/style/MissingParameterInQlDoc/Foo.qll new file mode 100644 index 00000000000..13509dbe521 --- /dev/null +++ b/ql/ql/test/queries/style/MissingParameterInQlDoc/Foo.qll @@ -0,0 +1,14 @@ +/** `param1`, `param2`, and `param3` are the parameters. */ +predicate test1(int param1, int param2, int param3) { none() } // OK + +/** `param1`, `par2` */ +predicate test2(int param1, int param2) { none() } // NOT OK - `par2` is not a parameter, and `param2` has no documentation + +/** `param1`, `par2 + par3` */ +predicate test3(int param1, int par2, int par3) { none() } // OK + +/** this mentions no parameters */ +predicate test4(int param1, int param2) { none() } // OK - the QLDoc mentions none of the parameters, that's OK + +/** the param1 parameter is mentioned in a non-code block, but the `par2` parameter is misspelled */ +predicate test5(int param1, int param2) { none() } // NOT OK - the `param1` parameter is "documented" in clear text, but `par2` is misspelled diff --git a/ql/ql/test/queries/style/MissingParameterInQlDoc/MissingParameterInQlDoc.expected b/ql/ql/test/queries/style/MissingParameterInQlDoc/MissingParameterInQlDoc.expected new file mode 100644 index 00000000000..cca5b457ec4 --- /dev/null +++ b/ql/ql/test/queries/style/MissingParameterInQlDoc/MissingParameterInQlDoc.expected @@ -0,0 +1,2 @@ +| Foo.qll:5:1:5:50 | ClasslessPredicate test2 | The QLDoc has no documentation for param2, but the QLDoc mentions par2 | +| Foo.qll:14:1:14:50 | ClasslessPredicate test5 | The QLDoc has no documentation for param2, but the QLDoc mentions par2 | diff --git a/ql/ql/test/queries/style/MissingParameterInQlDoc/MissingParameterInQlDoc.qlref b/ql/ql/test/queries/style/MissingParameterInQlDoc/MissingParameterInQlDoc.qlref new file mode 100644 index 00000000000..0539e4f5de2 --- /dev/null +++ b/ql/ql/test/queries/style/MissingParameterInQlDoc/MissingParameterInQlDoc.qlref @@ -0,0 +1 @@ +queries/style/MissingParameterInQlDoc.ql \ No newline at end of file From efba220b45f42f6643fdc55b4714cbb269535ee2 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Sun, 19 Dec 2021 23:01:53 +0100 Subject: [PATCH 004/505] JS: fix most `ql/missing-parameter-qldoc` issues --- .../ql/lib/semmle/javascript/BasicBlocks.qll | 2 +- .../semmle/javascript/CharacterEscapes.qll | 2 +- .../lib/semmle/javascript/ES2015Modules.qll | 2 +- javascript/ql/lib/semmle/javascript/Paths.qll | 4 ++-- .../lib/semmle/javascript/RangeAnalysis.qll | 22 +++++++++---------- javascript/ql/lib/semmle/javascript/SSA.qll | 2 +- .../javascript/dataflow/Configuration.qll | 12 +++++----- .../semmle/javascript/dataflow/DataFlow.qll | 2 +- .../semmle/javascript/dataflow/Portals.qll | 2 +- .../javascript/dataflow/TaintTracking.qll | 2 +- .../AngularJS/AngularJSExpressions.qll | 6 ++--- .../AngularJS/ServiceDefinitions.qll | 4 ++-- .../semmle/javascript/frameworks/Fastify.qll | 2 +- .../javascript/frameworks/NodeJSLib.qll | 8 +++---- .../security/performance/ReDoSUtil.qll | 2 +- .../src/Declarations/DeadStoreOfProperty.ql | 4 ++-- .../TemplateSyntaxInStringLiteral.ql | 4 ++-- javascript/ql/src/Metrics/ES20xxFeatures.qll | 2 +- javascript/ql/src/NodeJS/InvalidExport.ql | 8 +++---- .../Security/CWE-020/HostnameRegexpShared.qll | 2 +- .../CWE-020/IncompleteUrlSchemeCheck.ql | 4 ++-- .../Security/CWE-020/IncorrectSuffixCheck.ql | 2 +- javascript/ql/src/definitions.qll | 2 +- javascript/ql/src/external/DefectFilter.qll | 4 ++-- javascript/ql/src/external/MetricFilter.qll | 4 ++-- 25 files changed, 55 insertions(+), 55 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/BasicBlocks.qll b/javascript/ql/lib/semmle/javascript/BasicBlocks.qll index 2b02b6b6486..6e6579d6f7e 100644 --- a/javascript/ql/lib/semmle/javascript/BasicBlocks.qll +++ b/javascript/ql/lib/semmle/javascript/BasicBlocks.qll @@ -146,7 +146,7 @@ class BasicBlock extends @cfg_node, NodeInStmtContainer { /** Holds if this basic block uses variable `v` in its `i`th node `u`. */ predicate useAt(int i, Variable v, VarUse u) { useAt(this, i, v, u) } - /** Holds if this basic block defines variable `v` in its `i`th node `u`. */ + /** Holds if this basic block defines variable `v` in its `i`th node `d`. */ predicate defAt(int i, Variable v, VarDef d) { defAt(this, i, v, d) } /** diff --git a/javascript/ql/lib/semmle/javascript/CharacterEscapes.qll b/javascript/ql/lib/semmle/javascript/CharacterEscapes.qll index 1a19112cee3..5c8dd2bdd06 100644 --- a/javascript/ql/lib/semmle/javascript/CharacterEscapes.qll +++ b/javascript/ql/lib/semmle/javascript/CharacterEscapes.qll @@ -75,7 +75,7 @@ module CharacterEscapes { } /** - * Gets a character in `n` that is preceded by a single useless backslash, resulting in a likely regular expression mistake explained by `mistake`. + * Gets a character in `src` that is preceded by a single useless backslash, resulting in a likely regular expression mistake explained by `mistake`. * * The character is the `i`th character of the raw string value of `rawStringNode`. */ diff --git a/javascript/ql/lib/semmle/javascript/ES2015Modules.qll b/javascript/ql/lib/semmle/javascript/ES2015Modules.qll index 7ee6311393b..e584167a73b 100644 --- a/javascript/ql/lib/semmle/javascript/ES2015Modules.qll +++ b/javascript/ql/lib/semmle/javascript/ES2015Modules.qll @@ -337,7 +337,7 @@ class BulkReExportDeclaration extends ReExportDeclaration, @export_all_declarati } /** - * Holds if the given bulk export should not re-export `name` because there is an explicit export + * Holds if the given bulk export `reExport` should not re-export `name` because there is an explicit export * of that name in the same module. * * At compile time, shadowing works across declaration spaces. diff --git a/javascript/ql/lib/semmle/javascript/Paths.qll b/javascript/ql/lib/semmle/javascript/Paths.qll index e22d5ad6132..7574fe1e301 100644 --- a/javascript/ql/lib/semmle/javascript/Paths.qll +++ b/javascript/ql/lib/semmle/javascript/Paths.qll @@ -180,7 +180,7 @@ private Path resolveUpTo(PathString p, int n, Folder root, boolean inTS) { } /** - * Gets the `i`th component of the path `str`, where `base` is the resolved path one level up. + * Gets the `n`th component of the path `str`, where `base` is the resolved path one level up. * Supports that the root directory might be compiled output from TypeScript. * `inTS` is true if the result is TypeScript that is compiled into the path specified by `str`. */ @@ -227,7 +227,7 @@ private module TypeScriptOutDir { } /** - * Gets the `outDir` option from a tsconfig file from the folder `parent`. + * Gets the "outDir" option from a `tsconfig` file from the folder `parent`. */ private string getOutDir(JsonObject tsconfig, Folder parent) { tsconfig.getFile().getBaseName().regexpMatch("tsconfig.*\\.json") and diff --git a/javascript/ql/lib/semmle/javascript/RangeAnalysis.qll b/javascript/ql/lib/semmle/javascript/RangeAnalysis.qll index 5c15ea3d3aa..9d8b3967b1c 100644 --- a/javascript/ql/lib/semmle/javascript/RangeAnalysis.qll +++ b/javascript/ql/lib/semmle/javascript/RangeAnalysis.qll @@ -260,7 +260,7 @@ module RangeAnalysis { } /** - * Holds if the given comparison can be modeled as `A B + bias` where `` is the comparison operator, + * Holds if the given `comparison` can be modeled as `A B + bias` where `` is the comparison operator, * and `A` is `a * asign` and likewise `B` is `b * bsign`. */ predicate linearComparison( @@ -310,18 +310,18 @@ module RangeAnalysis { * Holds if `guard` asserts that the outcome of `A B + bias` is true, where `` is a comparison operator. */ predicate linearComparisonGuard( - ConditionGuardNode guard, DataFlow::Node a, int asign, string operator, DataFlow::Node b, - int bsign, Bias bias + ConditionGuardNode guard, DataFlow::Node a, int asign, string op, DataFlow::Node b, int bsign, + Bias bias ) { exists(Comparison compare | compare = guard.getTest().flow().getImmediatePredecessor*().asExpr() and linearComparison(compare, a, asign, b, bsign, bias) and ( - guard.getOutcome() = true and operator = compare.getOperator() + guard.getOutcome() = true and op = compare.getOperator() or not hasNaNIndicator(guard.getContainer()) and guard.getOutcome() = false and - operator = negateOperator(compare.getOperator()) + op = negateOperator(compare.getOperator()) ) ) } @@ -657,13 +657,13 @@ module RangeAnalysis { */ pragma[noopt] private predicate reachableByNegativeEdges( - DataFlow::Node a, int asign, DataFlow::Node b, int bsign, ControlFlowNode cfg + DataFlow::Node src, int asign, DataFlow::Node dst, int bsign, ControlFlowNode cfg ) { - negativeEdge(a, asign, b, bsign, cfg) + negativeEdge(src, asign, dst, bsign, cfg) or exists(DataFlow::Node mid, int midx, ControlFlowNode midcfg | - reachableByNegativeEdges(a, asign, mid, midx, cfg) and - negativeEdge(mid, midx, b, bsign, midcfg) and + reachableByNegativeEdges(src, asign, mid, midx, cfg) and + negativeEdge(mid, midx, dst, bsign, midcfg) and exists(BasicBlock bb, int i, int j | bb.getNode(i) = midcfg and bb.getNode(j) = cfg and @@ -676,8 +676,8 @@ module RangeAnalysis { DataFlow::Node mid, int midx, ControlFlowNode midcfg, BasicBlock midBB, ReachableBasicBlock midRBB, BasicBlock cfgBB | - reachableByNegativeEdges(a, asign, mid, midx, cfg) and - negativeEdge(mid, midx, b, bsign, midcfg) and + reachableByNegativeEdges(src, asign, mid, midx, cfg) and + negativeEdge(mid, midx, dst, bsign, midcfg) and midBB = midcfg.getBasicBlock() and midRBB = midBB.(ReachableBasicBlock) and cfgBB = cfg.getBasicBlock() and diff --git a/javascript/ql/lib/semmle/javascript/SSA.qll b/javascript/ql/lib/semmle/javascript/SSA.qll index 41831a282ac..8e60fb0c3e4 100644 --- a/javascript/ql/lib/semmle/javascript/SSA.qll +++ b/javascript/ql/lib/semmle/javascript/SSA.qll @@ -501,7 +501,7 @@ class SsaExplicitDefinition extends SsaDefinition, TExplicitDef { } /** This SSA definition corresponds to the definition of `v` at `def`. */ - predicate defines(VarDef d, SsaSourceVariable v) { this = TExplicitDef(_, _, d, v) } + predicate defines(VarDef def, SsaSourceVariable v) { this = TExplicitDef(_, _, def, v) } /** Gets the variable definition wrapped by this SSA definition. */ VarDef getDef() { this = TExplicitDef(_, _, result, _) } diff --git a/javascript/ql/lib/semmle/javascript/dataflow/Configuration.qll b/javascript/ql/lib/semmle/javascript/dataflow/Configuration.qll index 5a9b3de4a9b..48ebd583c83 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/Configuration.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/Configuration.qll @@ -353,7 +353,7 @@ abstract class BarrierGuardNode extends DataFlow::Node { } /** - * Holds if data flow node `nd` acts as a barrier for data flow. + * Holds if data flow node `guard` acts as a barrier for data flow. * * `label` is bound to the blocked label, or the empty string if all labels should be blocked. */ @@ -382,7 +382,7 @@ private predicate barrierGuardIsRelevant(BarrierGuardNode guard) { } /** - * Holds if data flow node `nd` acts as a barrier for data flow due to aliasing through + * Holds if data flow node `guard` acts as a barrier for data flow due to aliasing through * an access path. * * `label` is bound to the blocked label, or the empty string if all labels should be blocked. @@ -1155,7 +1155,7 @@ private predicate appendStep( } /** - * Holds if a function invoked at `invk` may return an expression into which `input`, + * Holds if a function invoked at `output` may return an expression into which `input`, * which is either an argument or a definition captured by the function, flows under * configuration `cfg`, possibly through callees. */ @@ -1391,7 +1391,7 @@ private predicate reachableFromStoreBase( } /** - * Holds if `base` is the base of a write to property `prop`, and `nd` is reachable + * Holds if `base` is the base of a write to property `endProp`, and `nd` is reachable * from `base` under configuration `cfg` (possibly through callees) along a path whose * last step is summarized by `newSummary`, and the previous steps are summarized * by `oldSummary`. @@ -1752,7 +1752,7 @@ class PathNode extends TPathNode { this = MkSinkNode(nd, cfg) } - /** Holds if this path node wraps data-flow node `nd` and configuration `c`. */ + /** Holds if this path node wraps data-flow node `n` and configuration `c`. */ predicate wraps(DataFlow::Node n, DataFlow::Configuration c) { nd = n and cfg = c } /** Gets the underlying configuration of this path node. */ @@ -1867,7 +1867,7 @@ class MidPathNode extends PathNode, MkMidNode { MidPathNode() { this = MkMidNode(nd, cfg, summary) } - /** Holds if this path node wraps data-flow node `nd`, configuration `c` and summary `s`. */ + /** Holds if this path node wraps data-flow node `n`, configuration `c` and summary `s`. */ predicate wraps(DataFlow::Node n, DataFlow::Configuration c, PathSummary s) { nd = n and cfg = c and summary = s } diff --git a/javascript/ql/lib/semmle/javascript/dataflow/DataFlow.qll b/javascript/ql/lib/semmle/javascript/dataflow/DataFlow.qll index 247c9dfd319..eda5c2ff54f 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/DataFlow.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/DataFlow.qll @@ -1613,7 +1613,7 @@ module DataFlow { } /** - * Holds if the flow information for this node is incomplete. + * Holds if the flow information for the node `nd`. * * This predicate holds if there may be a source flow node from which data flows into * this node, but that node is not a result of `getALocalSource()` due to analysis incompleteness. diff --git a/javascript/ql/lib/semmle/javascript/dataflow/Portals.qll b/javascript/ql/lib/semmle/javascript/dataflow/Portals.qll index 299819de4cd..3a8e0b477fb 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/Portals.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/Portals.qll @@ -498,7 +498,7 @@ private module ReturnPortal { invk = callee.getAnExitNode(isRemote).getAnInvocation() } - /** Holds if `ret` is a return node of a function flowing through `callee`. */ + /** Holds if `ret` is a return node of a function flowing through `base`. */ predicate returns(Portal base, DataFlow::Node ret, boolean escapes) { ret = base.getAnEntryNode(escapes).getALocalSource().(DataFlow::FunctionNode).getAReturn() } diff --git a/javascript/ql/lib/semmle/javascript/dataflow/TaintTracking.qll b/javascript/ql/lib/semmle/javascript/dataflow/TaintTracking.qll index 184e8a255a7..45a8920cfd8 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/TaintTracking.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/TaintTracking.qll @@ -831,7 +831,7 @@ module TaintTracking { } /** - * Holds if the property `loadStep` should be copied from the object `pred` to the property `storeStep` of object `succ`. + * Holds if the property `loadProp` should be copied from the object `pred` to the property `storeProp` of object `succ`. * * This step is used to copy the value of our pseudo-property that can later be accessed using a `get` or `getAll` call. * For an expression `url.searchParams`, the property `hiddenUrlPseudoProperty()` from the `url` object is stored in the property `getableUrlPseudoProperty()` on `url.searchParams`. diff --git a/javascript/ql/lib/semmle/javascript/frameworks/AngularJS/AngularJSExpressions.qll b/javascript/ql/lib/semmle/javascript/frameworks/AngularJS/AngularJSExpressions.qll index 56fca49cd10..050c123e30a 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/AngularJS/AngularJSExpressions.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/AngularJS/AngularJSExpressions.qll @@ -15,11 +15,11 @@ import javascript abstract class NgSourceProvider extends Locatable { /** * Holds if this element provides the source as `src` for an AngularJS expression at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. + * The location spans column `startColumn` of line `startLine` to + * column `endColumn` of line `endLine` in file `filepath`. */ abstract predicate providesSourceAt( - string src, string path, int startLine, int startColumn, int endLine, int endColumn + string src, string filepath, int startLine, int startColumn, int endLine, int endColumn ); /** diff --git a/javascript/ql/lib/semmle/javascript/frameworks/AngularJS/ServiceDefinitions.qll b/javascript/ql/lib/semmle/javascript/frameworks/AngularJS/ServiceDefinitions.qll index dcce784cd1a..6d421de851c 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/AngularJS/ServiceDefinitions.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/AngularJS/ServiceDefinitions.qll @@ -278,11 +278,11 @@ abstract private class CustomSpecialServiceDefinition extends CustomServiceDefin bindingset[moduleMethodName] private predicate isCustomServiceDefinitionOnModule( DataFlow::CallNode mce, string moduleMethodName, string serviceName, - DataFlow::Node factoryArgument + DataFlow::Node factoryFunction ) { mce = moduleRef(_).getAMethodCall(moduleMethodName) and mce.getArgument(0).asExpr().mayHaveStringValue(serviceName) and - factoryArgument = mce.getArgument(1) + factoryFunction = mce.getArgument(1) } pragma[inline] diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Fastify.qll b/javascript/ql/lib/semmle/javascript/frameworks/Fastify.qll index a0007de194d..3516935cf54 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Fastify.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Fastify.qll @@ -299,7 +299,7 @@ module Fastify { } /** - * Holds if `rh` uses `plugin`. + * Holds if `rh` uses `middleware`. */ private predicate usesMiddleware(RouteHandler rh, DataFlow::SourceNode middleware) { exists(RouteSetup setup | diff --git a/javascript/ql/lib/semmle/javascript/frameworks/NodeJSLib.qll b/javascript/ql/lib/semmle/javascript/frameworks/NodeJSLib.qll index 8f2576d58d4..0c5b9bcdfa2 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/NodeJSLib.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/NodeJSLib.qll @@ -474,17 +474,17 @@ module NodeJSLib { * that receives the data. * * We determine this by looking for an externs declaration for - * `fs.methodName` where the `i`th parameter's name is `data` or + * `fs.methodName` where the `i`th parameter's name (`paramName`) is `data` or * `buffer` or a `callback`. */ - private predicate fsDataParam(string methodName, int i, string n) { + private predicate fsDataParam(string methodName, int i, string paramName) { exists(ExternalMemberDecl decl, Function f, JSDocParamTag p | decl.hasQualifiedName("fs", methodName) and f = decl.getInit() and p.getDocumentedParameter() = f.getParameter(i).getAVariable() and - n = p.getName().toLowerCase() + paramName = p.getName().toLowerCase() | - n = "data" or n = "buffer" or n = "callback" + paramName = ["data", "buffer", "callback"] ) } diff --git a/javascript/ql/lib/semmle/javascript/security/performance/ReDoSUtil.qll b/javascript/ql/lib/semmle/javascript/security/performance/ReDoSUtil.qll index 91b2d1d0378..b05435cf1f4 100644 --- a/javascript/ql/lib/semmle/javascript/security/performance/ReDoSUtil.qll +++ b/javascript/ql/lib/semmle/javascript/security/performance/ReDoSUtil.qll @@ -32,7 +32,7 @@ abstract class ReDoSConfiguration extends string { } /** - * Holds if repeating `pump' starting at `state` is a candidate for causing backtracking. + * Holds if repeating `pump` starting at `state` is a candidate for causing backtracking. * No check whether a rejected suffix exists has been made. */ private predicate isReDoSCandidate(State state, string pump) { diff --git a/javascript/ql/src/Declarations/DeadStoreOfProperty.ql b/javascript/ql/src/Declarations/DeadStoreOfProperty.ql index 48b574f8cd1..c8cb0d8536e 100644 --- a/javascript/ql/src/Declarations/DeadStoreOfProperty.ql +++ b/javascript/ql/src/Declarations/DeadStoreOfProperty.ql @@ -154,7 +154,7 @@ predicate maybeAssignsAccessedPropInBlock(DataFlow::PropWrite assign, boolean af */ private module PurityCheck { /** - * Holds if a ControlFlowNode `c` is before an impure expression inside `bb`. + * Holds if `write` is before an impure expression inside `bb`. */ predicate isBeforeImpure(DataFlow::PropWrite write, ReachableBasicBlock bb) { getANodeAfterWrite(write, bb).(Expr).isImpure() @@ -181,7 +181,7 @@ private module PurityCheck { } /** - * Holds if a ControlFlowNode `c` is after an impure expression inside `bb`. + * Holds if `write` is after an impure expression inside `bb`. */ predicate isAfterImpure(DataFlow::PropWrite write, ReachableBasicBlock bb) { getANodeBeforeWrite(write, bb).(Expr).isImpure() diff --git a/javascript/ql/src/LanguageFeatures/TemplateSyntaxInStringLiteral.ql b/javascript/ql/src/LanguageFeatures/TemplateSyntaxInStringLiteral.ql index daa34825939..f22b9779560 100644 --- a/javascript/ql/src/LanguageFeatures/TemplateSyntaxInStringLiteral.ql +++ b/javascript/ql/src/LanguageFeatures/TemplateSyntaxInStringLiteral.ql @@ -84,10 +84,10 @@ predicate hasObjectProvidingTemplateVariables(CandidateStringLiteral lit) { * Gets a declaration of variable `v` in `tl`, where `v` has the given `name` and * belongs to `scope`. */ -VarDecl getDeclIn(Variable v, Scope s, string name, CandidateTopLevel tl) { +VarDecl getDeclIn(Variable v, Scope scope, string name, CandidateTopLevel tl) { v.getName() = name and v.getADeclaration() = result and - v.getScope() = s and + v.getScope() = scope and result.getTopLevel() = tl } diff --git a/javascript/ql/src/Metrics/ES20xxFeatures.qll b/javascript/ql/src/Metrics/ES20xxFeatures.qll index 8069ba5e50f..4aaed2d0fda 100644 --- a/javascript/ql/src/Metrics/ES20xxFeatures.qll +++ b/javascript/ql/src/Metrics/ES20xxFeatures.qll @@ -6,7 +6,7 @@ import javascript /** - * Holds if `nd` is a use of a feature introduced in ECMAScript version `ver` + * Holds if `nd` is a use of a feature introduced in ECMAScript `version` * from the given category. * * Categories are taken from Kangax' [ECMAScript 6 compatibility table] diff --git a/javascript/ql/src/NodeJS/InvalidExport.ql b/javascript/ql/src/NodeJS/InvalidExport.ql index 9daa363e888..e0b4a73fd69 100644 --- a/javascript/ql/src/NodeJS/InvalidExport.ql +++ b/javascript/ql/src/NodeJS/InvalidExport.ql @@ -16,14 +16,14 @@ import javascript /** * Holds if `assign` assigns the value of `nd` to `exportsVar`, which is an `exports` variable */ -predicate exportsAssign(Assignment assgn, Variable exportsVar, DataFlow::Node nd) { +predicate exportsAssign(Assignment assign, Variable exportsVar, DataFlow::Node nd) { exists(NodeModule m | exportsVar = m.getScope().getVariable("exports") and - assgn.getLhs() = exportsVar.getAnAccess() and - nd = assgn.getRhs().flow() + assign.getLhs() = exportsVar.getAnAccess() and + nd = assign.getRhs().flow() ) or - exportsAssign(assgn, exportsVar, nd.getASuccessor()) + exportsAssign(assign, exportsVar, nd.getASuccessor()) } /** diff --git a/javascript/ql/src/Security/CWE-020/HostnameRegexpShared.qll b/javascript/ql/src/Security/CWE-020/HostnameRegexpShared.qll index 97d75d4a524..428f8992bc7 100644 --- a/javascript/ql/src/Security/CWE-020/HostnameRegexpShared.qll +++ b/javascript/ql/src/Security/CWE-020/HostnameRegexpShared.qll @@ -53,7 +53,7 @@ predicate matchesBeginningOfString(RegExpTerm term) { } /** - * Holds if the given sequence contains top-level domain preceded by a dot, such as `.com`, + * Holds if the given sequence `seq` contains top-level domain preceded by a dot, such as `.com`, * excluding cases where this is at the very beginning of the regexp. * * `i` is bound to the index of the last child in the top-level domain part. diff --git a/javascript/ql/src/Security/CWE-020/IncompleteUrlSchemeCheck.ql b/javascript/ql/src/Security/CWE-020/IncompleteUrlSchemeCheck.ql index c5fb503d176..b84e9730ed4 100644 --- a/javascript/ql/src/Security/CWE-020/IncompleteUrlSchemeCheck.ql +++ b/javascript/ql/src/Security/CWE-020/IncompleteUrlSchemeCheck.ql @@ -88,8 +88,8 @@ DataFlow::Node schemeCheck(DataFlow::Node nd, DangerousScheme scheme) { } /** Gets a data-flow node that checks an instance of `ap` against the given `scheme`. */ -DataFlow::Node schemeCheckOn(DataFlow::SourceNode root, string path, DangerousScheme scheme) { - result = schemeCheck(AccessPath::getAReferenceTo(root, path), scheme) +DataFlow::Node schemeCheckOn(DataFlow::SourceNode root, string ap, DangerousScheme scheme) { + result = schemeCheck(AccessPath::getAReferenceTo(root, ap), scheme) } from DataFlow::SourceNode root, string path, int n diff --git a/javascript/ql/src/Security/CWE-020/IncorrectSuffixCheck.ql b/javascript/ql/src/Security/CWE-020/IncorrectSuffixCheck.ql index 03a7a75828b..650b71dd62f 100644 --- a/javascript/ql/src/Security/CWE-020/IncorrectSuffixCheck.ql +++ b/javascript/ql/src/Security/CWE-020/IncorrectSuffixCheck.ql @@ -84,7 +84,7 @@ class LiteralLengthExpr extends DotExpr { } /** - * Holds if `length` is derived from the length of the given `indexOf`-operand. + * Holds if `length` is derived from the length of the given indexOf `operand`. */ predicate isDerivedFromLength(DataFlow::Node length, DataFlow::Node operand) { exists(IndexOfCall call | operand = call.getAnOperand() | diff --git a/javascript/ql/src/definitions.qll b/javascript/ql/src/definitions.qll index 4d0c0d50176..7b4806b1478 100644 --- a/javascript/ql/src/definitions.qll +++ b/javascript/ql/src/definitions.qll @@ -45,7 +45,7 @@ private predicate variableDefLookup(VarAccess va, AstNode def, string kind) { /** * Holds if variable access `va` is of kind `kind` and refers to the - * variable declaration. + * variable declaration `decl`. * * For example, in the statement `var x = 42, y = x;`, the initializing * expression of `y` is a variable access `x` of kind `"V"` that refers to diff --git a/javascript/ql/src/external/DefectFilter.qll b/javascript/ql/src/external/DefectFilter.qll index 40c9527e96d..558d5ef77b6 100644 --- a/javascript/ql/src/external/DefectFilter.qll +++ b/javascript/ql/src/external/DefectFilter.qll @@ -5,8 +5,8 @@ import semmle.javascript.Files /** * Holds if `id` in the opaque identifier of a result reported by query `queryPath`, * such that `message` is the associated message and the location of the result spans - * column `startcolumn` of line `startline` to column `endcolumn` of line `endline` - * in file `filepath`. + * column `startcol` of line `startline` to column `endcol` of line `endline` + * in `file`. * * For more information, see [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ diff --git a/javascript/ql/src/external/MetricFilter.qll b/javascript/ql/src/external/MetricFilter.qll index a857a4fad3e..f195060b60c 100644 --- a/javascript/ql/src/external/MetricFilter.qll +++ b/javascript/ql/src/external/MetricFilter.qll @@ -5,8 +5,8 @@ import javascript /** * Holds if `id` in the opaque identifier of a result reported by query `queryPath`, * such that `value` is the reported metric value and the location of the result spans - * column `startcolumn` of line `startline` to column `endcolumn` of line `endline` - * in file `filepath`. + * column `startcol` of line `startline` to column `endcol` of line `endline` + * in `file`. * * For more information, see [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ From 53760799fc2c18cc929d7b359d54e2b570aa188b Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Sun, 19 Dec 2021 23:03:36 +0100 Subject: [PATCH 005/505] sync files --- python/ql/lib/semmle/python/security/performance/ReDoSUtil.qll | 2 +- ruby/ql/lib/codeql/ruby/security/performance/ReDoSUtil.qll | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/python/ql/lib/semmle/python/security/performance/ReDoSUtil.qll b/python/ql/lib/semmle/python/security/performance/ReDoSUtil.qll index 91b2d1d0378..b05435cf1f4 100644 --- a/python/ql/lib/semmle/python/security/performance/ReDoSUtil.qll +++ b/python/ql/lib/semmle/python/security/performance/ReDoSUtil.qll @@ -32,7 +32,7 @@ abstract class ReDoSConfiguration extends string { } /** - * Holds if repeating `pump' starting at `state` is a candidate for causing backtracking. + * Holds if repeating `pump` starting at `state` is a candidate for causing backtracking. * No check whether a rejected suffix exists has been made. */ private predicate isReDoSCandidate(State state, string pump) { diff --git a/ruby/ql/lib/codeql/ruby/security/performance/ReDoSUtil.qll b/ruby/ql/lib/codeql/ruby/security/performance/ReDoSUtil.qll index 91b2d1d0378..b05435cf1f4 100644 --- a/ruby/ql/lib/codeql/ruby/security/performance/ReDoSUtil.qll +++ b/ruby/ql/lib/codeql/ruby/security/performance/ReDoSUtil.qll @@ -32,7 +32,7 @@ abstract class ReDoSConfiguration extends string { } /** - * Holds if repeating `pump' starting at `state` is a candidate for causing backtracking. + * Holds if repeating `pump` starting at `state` is a candidate for causing backtracking. * No check whether a rejected suffix exists has been made. */ private predicate isReDoSCandidate(State state, string pump) { From f204a411220eecd6816d6e600951e73a4bdfe58d Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 20 Dec 2021 11:15:21 +0100 Subject: [PATCH 006/505] QL: fix ql/missing-parameter-qldoc error in QL-for-QL --- ql/ql/src/codeql_ql/ast/internal/Module.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ql/ql/src/codeql_ql/ast/internal/Module.qll b/ql/ql/src/codeql_ql/ast/internal/Module.qll index 50416337bf4..225fa4c4d3a 100644 --- a/ql/ql/src/codeql_ql/ast/internal/Module.qll +++ b/ql/ql/src/codeql_ql/ast/internal/Module.qll @@ -239,7 +239,7 @@ boolean getPublicBool(AstNode n) { /** * Holds if `container` defines module `m` with name `name`. * - * `m` may be defined either directly or through `import`s. + * `m` may be defined either directly or through imports. */ private predicate definesModule( ContainerOrModule container, string name, ContainerOrModule m, boolean public From 3762ce2c72c3c13cf5e2ef680f829d00f3c18279 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 20 Dec 2021 13:46:02 +0100 Subject: [PATCH 007/505] QL: also report missing QLDoc for parameters when no parameters are documented --- .../queries/style/MissingParameterInQlDoc.ql | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/ql/ql/src/queries/style/MissingParameterInQlDoc.ql b/ql/ql/src/queries/style/MissingParameterInQlDoc.ql index 1fe962154f8..1b9f82a566e 100644 --- a/ql/ql/src/queries/style/MissingParameterInQlDoc.ql +++ b/ql/ql/src/queries/style/MissingParameterInQlDoc.ql @@ -42,34 +42,35 @@ private string getAMentionedNonParameter(Predicate p) { ) and result.regexpMatch("^[a-z]\\w+$") and not result.toLowerCase() = getAParameterName(p).toLowerCase() and - not result = ["true", "false", "NaN"] and // keywords - not result.regexpMatch("\\d+") and // numbers + not result = ["true", "false", "NaN", "this"] and // keywords + not result = getMentionedPredicates(p.getLocation().getFile()) and + // variables inside the predicate are also fine + not result = any(VarDecl var | var.getEnclosingPredicate() = p).getName() +} + +/** Gets the names of all predicates that are mentioned in `file`. */ +pragma[noinline] +private string getMentionedPredicates(File file) { // predicates get mentioned all the time, it's fine. - not result = - any(Predicate pred | pred.getLocation().getFile() = p.getLocation().getFile()).getName() and - // classes get mentioned all the time, it's fine. - not result = - any(TypeExpr t | t.getLocation().getFile() = p.getLocation().getFile()) - .getResolvedType() - .getName() + result = any(Predicate pred | pred.getLocation().getFile() = file).getName() or + result = any(Call c | c.getLocation().getFile() = file).getTarget().getName() } /** Gets a parameter name from `p` that is not mentioned in the qldoc. */ private string getAnUndocumentedParameter(Predicate p) { result = getAParameterName(p) and not result.toLowerCase() = getADocumentedParameter(p).toLowerCase() and - not result = ["config", "conf", "cfg"] // DataFlow configurations are often undocumented, and that's fine. -} - -/** Holds if `p` has documented parameters, but `param` is undocumented */ -private predicate missingDocumentation(Predicate p, string param) { - param = getAnUndocumentedParameter(p) and - exists(getADocumentedParameter(p)) + not result = ["config", "conf", "cfg"] and // DataFlow configurations are often undocumented, and that's fine. + not ( + // "the given" often refers to the first parameter. + p.getQLDoc().getContents().regexpMatch("(?s).*\\bthe given\\b.*") and + result = p.getParameter(0).getName() + ) } /** Gets the one string containing the undocumented parameters from `p` */ private string getUndocumentedParameters(Predicate p) { - result = strictconcat(string param | missingDocumentation(p, param) | param, ", or ") + result = strictconcat(string param | param = getAnUndocumentedParameter(p) | param, ", or ") } /** Gets the parameter-like names mentioned in the QLDoc of `p` that are not parameters. */ @@ -78,6 +79,7 @@ private string getMentionedNonParameters(Predicate p) { } from Predicate p +where not p.getLocation().getFile().getBaseName() = "Aliases.qll" // these are OK select p, "The QLDoc has no documentation for " + getUndocumentedParameters(p) + ", but the QLDoc mentions " + getMentionedNonParameters(p) From daed33f5afe8d5c0a9ea835be1264c160f37fde1 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 20 Dec 2021 13:46:15 +0100 Subject: [PATCH 008/505] JS: fix more instances of `ql/missing-parameter-qldoc` --- .../dataflow/TemplateInjection/TemplateInjection.ql | 2 +- javascript/ql/lib/semmle/javascript/Classes.qll | 2 +- javascript/ql/lib/semmle/javascript/ES2015Modules.qll | 2 +- javascript/ql/lib/semmle/javascript/PrintAst.qll | 2 +- javascript/ql/lib/semmle/javascript/Routing.qll | 2 +- javascript/ql/lib/semmle/javascript/TypeScript.qll | 2 +- .../ql/lib/semmle/javascript/frameworks/Bundling.qll | 6 +++--- .../javascript/frameworks/ConnectExpressShared.qll | 2 +- .../ql/lib/semmle/javascript/frameworks/NoSQL.qll | 6 +++--- .../ql/lib/semmle/javascript/frameworks/SocketIO.qll | 4 ++-- .../ql/lib/semmle/javascript/frameworks/jQuery.qll | 11 ++++++----- javascript/ql/src/RegExp/RegExpAlwaysMatches.ql | 8 ++++---- .../ql/src/Security/CWE-200/PrivateFileExposure.ql | 10 +++++----- javascript/ql/src/Security/CWE-384/SessionFixation.ql | 2 +- 14 files changed, 31 insertions(+), 30 deletions(-) diff --git a/javascript/ql/examples/queries/dataflow/TemplateInjection/TemplateInjection.ql b/javascript/ql/examples/queries/dataflow/TemplateInjection/TemplateInjection.ql index 2286fdc2121..ea4a99c69d8 100644 --- a/javascript/ql/examples/queries/dataflow/TemplateInjection/TemplateInjection.ql +++ b/javascript/ql/examples/queries/dataflow/TemplateInjection/TemplateInjection.ql @@ -14,7 +14,7 @@ import DataFlow::PathGraph /** * Gets the name of an unescaped placeholder in a lodash template. * - * For example, the string `

<%= title %>

` contains the placeholder `title`. + * For example, the string "

<%= title %>

" contains the placeholder "title". */ bindingset[s] string getAPlaceholderInString(string s) { diff --git a/javascript/ql/lib/semmle/javascript/Classes.qll b/javascript/ql/lib/semmle/javascript/Classes.qll index 309e2aae615..2b39eac9f42 100644 --- a/javascript/ql/lib/semmle/javascript/Classes.qll +++ b/javascript/ql/lib/semmle/javascript/Classes.qll @@ -172,7 +172,7 @@ class ClassDefinition extends @class_definition, ClassOrInterface, AST::ValueNod /** Gets the expression denoting the super class of the defined class, if any. */ override Expr getSuperClass() { result = this.getChildExpr(1) } - /** Gets the `n`th type from the `implements` clause of this class, starting at 0. */ + /** Gets the `i`th type from the `implements` clause of this class, starting at 0. */ override TypeExpr getSuperInterface(int i) { // AST indices for super interfaces: -1, -4, -7, ... exists(int astIndex | typeexprs(result, _, this, astIndex, _) | diff --git a/javascript/ql/lib/semmle/javascript/ES2015Modules.qll b/javascript/ql/lib/semmle/javascript/ES2015Modules.qll index e584167a73b..89e0f92a13d 100644 --- a/javascript/ql/lib/semmle/javascript/ES2015Modules.qll +++ b/javascript/ql/lib/semmle/javascript/ES2015Modules.qll @@ -54,7 +54,7 @@ private predicate hasNamedExports(ES2015Module mod) { } /** - * Holds if this module contains a `default` export. + * Holds if this module contains a default export. */ private predicate hasDefaultExport(ES2015Module mod) { // export default foo; diff --git a/javascript/ql/lib/semmle/javascript/PrintAst.qll b/javascript/ql/lib/semmle/javascript/PrintAst.qll index f60b19ccb4e..be762640327 100644 --- a/javascript/ql/lib/semmle/javascript/PrintAst.qll +++ b/javascript/ql/lib/semmle/javascript/PrintAst.qll @@ -195,7 +195,7 @@ private module PrintJavaScript { * Gets the `i`th child of `element`. * Can be overriden in subclasses to get more specific behavior for `getChild()`. */ - AstNode getChildNode(int childIndex) { result = getLocationSortedChild(element, childIndex) } + AstNode getChildNode(int i) { result = getLocationSortedChild(element, i) } } /** Provides predicates for pretty printing `AstNode`s. */ diff --git a/javascript/ql/lib/semmle/javascript/Routing.qll b/javascript/ql/lib/semmle/javascript/Routing.qll index ad75132f9f9..84c83956d07 100644 --- a/javascript/ql/lib/semmle/javascript/Routing.qll +++ b/javascript/ql/lib/semmle/javascript/Routing.qll @@ -229,7 +229,7 @@ module Routing { } /** - * Holds if `node` has processed the incoming request strictly prior to this node. + * Holds if `guard` has processed the incoming request strictly prior to this node. */ pragma[inline] private predicate isGuardedByNodeInternal(Node guard) { diff --git a/javascript/ql/lib/semmle/javascript/TypeScript.qll b/javascript/ql/lib/semmle/javascript/TypeScript.qll index 70a1ed98830..4f421eb5d4d 100644 --- a/javascript/ql/lib/semmle/javascript/TypeScript.qll +++ b/javascript/ql/lib/semmle/javascript/TypeScript.qll @@ -751,7 +751,7 @@ class TypeAccess extends @typeaccess, TypeExpr, TypeRef { } /** - * Gets a suitable name for the library imported by `import`. + * Gets a suitable name for the library imported by `imprt`. * * For relative imports, this is the snapshot-relative path to the imported module. * For non-relative imports, it is the import path itself. diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Bundling.qll b/javascript/ql/lib/semmle/javascript/frameworks/Bundling.qll index d7fd51c1b82..1315ac651d5 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Bundling.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Bundling.qll @@ -102,9 +102,9 @@ private predicate isBrowserifyDependencyMap(ObjectExpr deps) { * Holds if `m` is a function that looks like a bundled module created * by Webpack. * - * Parameters must be named either `module` or `exports`, - * or their name must contain the substring `webpack_require` - * or `webpack_module_template_argument`. + * Parameters must be named either "module" or "exports", + * or their name must contain the substring "webpack_require" + * or "webpack_module_template_argument". */ private predicate isWebpackModule(FunctionExpr m) { forex(Parameter parm | parm = m.getAParameter() | diff --git a/javascript/ql/lib/semmle/javascript/frameworks/ConnectExpressShared.qll b/javascript/ql/lib/semmle/javascript/frameworks/ConnectExpressShared.qll index 9da7c959cb0..489fcf550c4 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/ConnectExpressShared.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/ConnectExpressShared.qll @@ -50,7 +50,7 @@ module ConnectExpressShared { } /** - * Holds if `fun` appears to match the given signature based on parameter naming. + * Holds if `function` appears to match the given signature based on parameter naming. */ private predicate matchesSignature(Function function, RouteHandlerSignature sig) { function.getNumParameter() = sig.getArity() and diff --git a/javascript/ql/lib/semmle/javascript/frameworks/NoSQL.qll b/javascript/ql/lib/semmle/javascript/frameworks/NoSQL.qll index 6889161696f..fb64d054ed7 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/NoSQL.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/NoSQL.qll @@ -584,11 +584,11 @@ private module Minimongo { */ module CollectionMethodSignatures { /** - * Holds if Collection method `name` interprets parameter `n` as a query. + * Holds if Collection method `name` interprets parameter `queryArgIdx` as a query. */ - predicate interpretsArgumentAsQuery(string m, int queryArgIdx) { + predicate interpretsArgumentAsQuery(string name, int queryArgIdx) { // implements most of the MongoDB interface - MongoDB::CollectionMethodSignatures::interpretsArgumentAsQuery(m, queryArgIdx) + MongoDB::CollectionMethodSignatures::interpretsArgumentAsQuery(name, queryArgIdx) } } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/SocketIO.qll b/javascript/ql/lib/semmle/javascript/frameworks/SocketIO.qll index df761420e29..4ffdf1432ac 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/SocketIO.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/SocketIO.qll @@ -55,7 +55,7 @@ module SocketIO { /** Gets the namespace with the given path of this server. */ NamespaceObject getNamespace(string path) { result = MkNamespace(this, path) } - /** Gets a api node that may refer to the socket.io server created at `srv`. */ + /** Gets a api node that may refer to a socket.io server. */ private API::Node server() { result = node or @@ -144,7 +144,7 @@ module SocketIO { override NamespaceObject getNamespace() { result = ns } /** - * Gets a data flow node that may refer to the socket.io namespace created at `ns`. + * Gets a data flow node that may refer a the socket.io namespace. */ private API::Node namespace() { result = node diff --git a/javascript/ql/lib/semmle/javascript/frameworks/jQuery.qll b/javascript/ql/lib/semmle/javascript/frameworks/jQuery.qll index 28a01dba7ab..54833514e69 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/jQuery.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/jQuery.qll @@ -309,12 +309,13 @@ private module JQueryClientRequest { /** * Gets a node refering to the response contained in an `jqXHR` object. */ - private DataFlow::SourceNode getAResponseNodeFromAnXHRObject(DataFlow::SourceNode obj) { + private DataFlow::SourceNode getAResponseNodeFromAnXHRObject(DataFlow::SourceNode jqXHR) { result = - obj.getAPropertyRead(any(string s | - s = "responseText" or - s = "responseXML" - )) + jqXHR + .getAPropertyRead(any(string s | + s = "responseText" or + s = "responseXML" + )) } /** diff --git a/javascript/ql/src/RegExp/RegExpAlwaysMatches.ql b/javascript/ql/src/RegExp/RegExpAlwaysMatches.ql index e352617337f..06e0a8146ef 100644 --- a/javascript/ql/src/RegExp/RegExpAlwaysMatches.ql +++ b/javascript/ql/src/RegExp/RegExpAlwaysMatches.ql @@ -39,10 +39,10 @@ RegExpTerm getEffectiveRoot(RegExpTerm actualRoot) { /** * Holds if `term` contains an anchor on both ends. */ -predicate isPossiblyAnchoredOnBothEnds(RegExpSequence node) { - node.getAChild*() instanceof RegExpCaret and - node.getAChild*() instanceof RegExpDollar and - node.getNumChild() >= 2 +predicate isPossiblyAnchoredOnBothEnds(RegExpSequence term) { + term.getAChild*() instanceof RegExpCaret and + term.getAChild*() instanceof RegExpDollar and + term.getNumChild() >= 2 } /** diff --git a/javascript/ql/src/Security/CWE-200/PrivateFileExposure.ql b/javascript/ql/src/Security/CWE-200/PrivateFileExposure.ql index 99ca8cba8a7..add8679dee7 100644 --- a/javascript/ql/src/Security/CWE-200/PrivateFileExposure.ql +++ b/javascript/ql/src/Security/CWE-200/PrivateFileExposure.ql @@ -72,11 +72,11 @@ pragma[noinline] Folder getAPackageJsonFolder() { result = any(PackageJson json).getFile().getParentContainer() } /** - * Gets a reference to `dirname`, the home folder, the current working folder, or the root folder. + * Gets a reference to a directory that has a `package.json` in the same folder, the home folder, + * the current working folder, or the root folder. * All of these might cause information to be leaked. * - * For `dirname` that can happen if there is a `package.json` file in the same folder. - * It is assumed that the presence of a `package.json` file means that a `node_modules` folder can also exist. + * For the first case it is assumed that the presence of a `package.json` file means that a `node_modules` folder can also exist. * * For the root/home/working folder, they contain so much information that they must leak information somehow (e.g. ssh keys in the `~/.ssh` folder). */ @@ -108,7 +108,7 @@ DataFlow::Node getALeakingFolder(string description) { } /** - * Gets a data-flow node that represents a path to the private folder `path`. + * Gets a data-flow node that represents the private folder descriped by `description`. */ DataFlow::Node getAPrivateFolderPath(string description) { exists(string path | @@ -119,7 +119,7 @@ DataFlow::Node getAPrivateFolderPath(string description) { } /** - * Gest a call that serves the folder `path` to the public. + * Gest a call that serves the folder descriped by `description` to the public. */ DataFlow::CallNode servesAPrivateFolder(string description) { result = DataFlow::moduleMember(["express", "connect"], "static").getACall() and diff --git a/javascript/ql/src/Security/CWE-384/SessionFixation.ql b/javascript/ql/src/Security/CWE-384/SessionFixation.ql index aec00f22d45..7fccf16b01b 100644 --- a/javascript/ql/src/Security/CWE-384/SessionFixation.ql +++ b/javascript/ql/src/Security/CWE-384/SessionFixation.ql @@ -36,7 +36,7 @@ predicate isLoginSetup(Express::RouteSetup setup) { } /** - * Holds if `handler` regenerates its session using `req.session.regenerate`. + * Holds if `setup` regenerates its session using `req.session.regenerate`. */ pragma[inline] predicate regeneratesSession(Express::RouteSetup setup) { From 35c3c62f9e7e5acea9441e5989961ded1a290fd1 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 4 Jan 2022 13:42:30 +0100 Subject: [PATCH 009/505] apply suggestions from code review --- .../queries/dataflow/TemplateInjection/TemplateInjection.ql | 2 +- ql/ql/src/queries/style/MissingParameterInQlDoc.ql | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/javascript/ql/examples/queries/dataflow/TemplateInjection/TemplateInjection.ql b/javascript/ql/examples/queries/dataflow/TemplateInjection/TemplateInjection.ql index ea4a99c69d8..b146b19e54d 100644 --- a/javascript/ql/examples/queries/dataflow/TemplateInjection/TemplateInjection.ql +++ b/javascript/ql/examples/queries/dataflow/TemplateInjection/TemplateInjection.ql @@ -14,7 +14,7 @@ import DataFlow::PathGraph /** * Gets the name of an unescaped placeholder in a lodash template. * - * For example, the string "

<%= title %>

" contains the placeholder "title". + * For example, the string `"

<%= title %>

"` contains the placeholder "title". */ bindingset[s] string getAPlaceholderInString(string s) { diff --git a/ql/ql/src/queries/style/MissingParameterInQlDoc.ql b/ql/ql/src/queries/style/MissingParameterInQlDoc.ql index 1b9f82a566e..ccf587d9611 100644 --- a/ql/ql/src/queries/style/MissingParameterInQlDoc.ql +++ b/ql/ql/src/queries/style/MissingParameterInQlDoc.ql @@ -42,7 +42,8 @@ private string getAMentionedNonParameter(Predicate p) { ) and result.regexpMatch("^[a-z]\\w+$") and not result.toLowerCase() = getAParameterName(p).toLowerCase() and - not result = ["true", "false", "NaN", "this"] and // keywords + not result = ["true", "false", "NaN", "this", "forall", "exists", "null", "break", "return"] and // keywords + not result = any(Aggregate a).getKind() and // min, max, sum, count, etc. not result = getMentionedPredicates(p.getLocation().getFile()) and // variables inside the predicate are also fine not result = any(VarDecl var | var.getEnclosingPredicate() = p).getName() From 86c8737250596bc5988b033ce03339b2e0233639 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 4 Jan 2022 13:51:49 +0100 Subject: [PATCH 010/505] remove string constants from mentioned non-params --- ql/ql/src/queries/style/MissingParameterInQlDoc.ql | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ql/ql/src/queries/style/MissingParameterInQlDoc.ql b/ql/ql/src/queries/style/MissingParameterInQlDoc.ql index ccf587d9611..d4acb329a23 100644 --- a/ql/ql/src/queries/style/MissingParameterInQlDoc.ql +++ b/ql/ql/src/queries/style/MissingParameterInQlDoc.ql @@ -44,17 +44,18 @@ private string getAMentionedNonParameter(Predicate p) { not result.toLowerCase() = getAParameterName(p).toLowerCase() and not result = ["true", "false", "NaN", "this", "forall", "exists", "null", "break", "return"] and // keywords not result = any(Aggregate a).getKind() and // min, max, sum, count, etc. - not result = getMentionedPredicates(p.getLocation().getFile()) and + not result = getMentionedThings(p.getLocation().getFile()) and // variables inside the predicate are also fine not result = any(VarDecl var | var.getEnclosingPredicate() = p).getName() } -/** Gets the names of all predicates that are mentioned in `file`. */ +/** Gets the names of all predicates and string constants that are mentioned in `file`. */ pragma[noinline] -private string getMentionedPredicates(File file) { +private string getMentionedThings(File file) { // predicates get mentioned all the time, it's fine. result = any(Predicate pred | pred.getLocation().getFile() = file).getName() or - result = any(Call c | c.getLocation().getFile() = file).getTarget().getName() + result = any(Call c | c.getLocation().getFile() = file).getTarget().getName() or + result = any(String s | s.getLocation().getFile() = file).getValue() } /** Gets a parameter name from `p` that is not mentioned in the qldoc. */ From 2a196611af2a199449eb26d9fb818e9e35d83e0c Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 4 Jan 2022 13:54:53 +0100 Subject: [PATCH 011/505] add `not` as a keyword --- ql/ql/src/queries/style/MissingParameterInQlDoc.ql | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ql/ql/src/queries/style/MissingParameterInQlDoc.ql b/ql/ql/src/queries/style/MissingParameterInQlDoc.ql index d4acb329a23..730b5a89a1f 100644 --- a/ql/ql/src/queries/style/MissingParameterInQlDoc.ql +++ b/ql/ql/src/queries/style/MissingParameterInQlDoc.ql @@ -42,7 +42,9 @@ private string getAMentionedNonParameter(Predicate p) { ) and result.regexpMatch("^[a-z]\\w+$") and not result.toLowerCase() = getAParameterName(p).toLowerCase() and - not result = ["true", "false", "NaN", "this", "forall", "exists", "null", "break", "return"] and // keywords + // keywords + not result = + ["true", "false", "NaN", "this", "forall", "exists", "null", "break", "return", "not"] and not result = any(Aggregate a).getKind() and // min, max, sum, count, etc. not result = getMentionedThings(p.getLocation().getFile()) and // variables inside the predicate are also fine From 4b50c68934df4faf20a3155e7c0d3dec9675e95f Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 4 Jan 2022 14:13:48 +0100 Subject: [PATCH 012/505] exclude annotation names --- ql/ql/src/queries/style/MissingParameterInQlDoc.ql | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ql/ql/src/queries/style/MissingParameterInQlDoc.ql b/ql/ql/src/queries/style/MissingParameterInQlDoc.ql index 730b5a89a1f..b378803822a 100644 --- a/ql/ql/src/queries/style/MissingParameterInQlDoc.ql +++ b/ql/ql/src/queries/style/MissingParameterInQlDoc.ql @@ -47,6 +47,8 @@ private string getAMentionedNonParameter(Predicate p) { ["true", "false", "NaN", "this", "forall", "exists", "null", "break", "return", "not"] and not result = any(Aggregate a).getKind() and // min, max, sum, count, etc. not result = getMentionedThings(p.getLocation().getFile()) and + not result = any(Annotation a).getName() and // private, final, etc. + not result = any(Annotation a).getArgs(_).getValue() and // noinline, etc. // variables inside the predicate are also fine not result = any(VarDecl var | var.getEnclosingPredicate() = p).getName() } From a7c69ba6abf9b6a1ab12a1bf8b9a8b3d84706841 Mon Sep 17 00:00:00 2001 From: ihsinme Date: Mon, 9 May 2022 13:15:27 +0000 Subject: [PATCH 013/505] create new branchihsinme-patch-87 in fork --- .../CWE/CWE-125/DangerousUseMbtowc.cpp | 6 + .../CWE/CWE-125/DangerousUseMbtowc.qhelp | 23 +++ .../CWE/CWE-125/DangerousUseMbtowc.ql | 106 ++++++++++ .../semmle/tests/DangerousUseMbtowc.expected | 8 + .../semmle/tests/DangerousUseMbtowc.qlref | 1 + .../CWE/CWE-125/semmle/tests/test.cpp | 194 ++++++++++++++++++ 6 files changed, 338 insertions(+) create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.cpp create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.qhelp create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.ql create mode 100644 cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousUseMbtowc.expected create mode 100644 cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousUseMbtowc.qlref create mode 100644 cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test.cpp diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.cpp b/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.cpp new file mode 100644 index 00000000000..3b71c4f3005 --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.cpp @@ -0,0 +1,6 @@ + +... +mbtowc(&wc, ptr, 4)); // BAD:we can get unpredictable results +... +mbtowc(&wc, ptr, MB_LEN_MAX); // GOOD +... diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.qhelp new file mode 100644 index 00000000000..8634b9b66ba --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.qhelp @@ -0,0 +1,23 @@ + + + +

Using function mbtowc with an invalid length argument can result in an out-of-bounds access error or unexpected result. If you are sure you are working with a null-terminated string, use the length macros, if not, use the correctly computed length.

+ +
+ + +

The following example shows the erroneous and corrected method of using function mbtowc.

+ + +
+ + +
  • + CERT Coding Standard: + ARR30-C. Do not form or use out-of-bounds pointers or array subscripts - SEI CERT C Coding Standard - Confluence. +
  • + +
    +
    \ No newline at end of file diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.ql b/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.ql new file mode 100644 index 00000000000..0940c899312 --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.ql @@ -0,0 +1,106 @@ +/** + * @name Dangerous use mbtowc. + * @description Using function mbtowc with an invalid length argument can result in an out-of-bounds access error or unexpected result. + * @kind problem + * @id cpp/dangerous-use-mbtowc + * @problem.severity warning + * @precision medium + * @tags correctness + * security + * external/cwe/cwe-125 + */ + +import cpp +import semmle.code.cpp.valuenumbering.GlobalValueNumbering + +/** Holds if there are indications that the variable is treated as a string. */ +predicate exprMayBeString(Expr exp) { + ( + exists(StringLiteral sl | globalValueNumber(exp) = globalValueNumber(sl)) + or + exists(FunctionCall fctmp | + globalValueNumber(fctmp.getAnArgument()) = globalValueNumber(exp) and + fctmp.getTarget().hasGlobalOrStdName(["strlen", "strcat", "strncat", "strcpy", "sptintf"]) + ) + or + exists(AssignExpr astmp | + astmp.getRValue().getValue() = "0" and + astmp.getLValue().(ArrayExpr).getArrayBase().(VariableAccess).getTarget() = + exp.(VariableAccess).getTarget() + ) + or + exists(ComparisonOperation cotmp, Expr exptmp1, Expr exptmp2 | + exptmp1.getValue() = "0" and + ( + exptmp2.(PointerDereferenceExpr).getOperand().(VariableAccess).getTarget() = + exp.(VariableAccess).getTarget() or + exptmp2.(ArrayExpr).getArrayBase().(VariableAccess).getTarget() = + exp.getAChild().(VariableAccess).getTarget() + ) and + cotmp.hasOperands(exptmp1, exptmp2) + ) + ) +} + +/** Holds if expression is constant or operator call `sizeof`. */ +predicate argConstOrSizeof(Expr exp) { + exp.getValue().toInt() > 1 or + exp.(SizeofTypeOperator).getTypeOperand().getSize() > 1 +} + +/** Holds if expression is macro. */ +predicate argMacro(Expr exp) { + exists(MacroInvocation matmp | + exp = matmp.getExpr() and + ( + matmp.getMacroName() = "MB_LEN_MAX" or + matmp.getMacroName() = "MB_CUR_MAX" + ) + ) +} + +from FunctionCall fc, string msg +where + exists(Loop lptmp | lptmp = fc.getEnclosingStmt().getParentStmt*()) and + fc.getTarget().hasGlobalOrStdName(["mbtowc", "mbrtowc"]) and + not fc.getArgument(0).isConstant() and + not fc.getArgument(1).isConstant() and + ( + exprMayBeString(fc.getArgument(1)) and + argConstOrSizeof(fc.getArgument(2)) and + fc.getArgument(2).getValue().toInt() < 5 and + not argMacro(fc.getArgument(2)) and + msg = "Size can be less than maximum character length, use macro MB_CUR_MAX." + or + not exprMayBeString(fc.getArgument(1)) and + ( + argConstOrSizeof(fc.getArgument(2)) + or + argMacro(fc.getArgument(2)) + or + exists(DecrementOperation dotmp | + globalValueNumber(dotmp.getAnOperand()) = globalValueNumber(fc.getArgument(2)) and + not exists(AssignSubExpr aetmp | + ( + aetmp.getLValue().(VariableAccess).getTarget() = + fc.getArgument(2).(VariableAccess).getTarget() or + globalValueNumber(aetmp.getLValue()) = globalValueNumber(fc.getArgument(2)) + ) and + globalValueNumber(aetmp.getRValue()) = globalValueNumber(fc) + ) + ) + ) and + msg = + "Access beyond the allocated memory is possible, the length can change without changing the pointer." + or + exists(AssignPointerAddExpr aetmp | + ( + aetmp.getLValue().(VariableAccess).getTarget() = + fc.getArgument(0).(VariableAccess).getTarget() or + globalValueNumber(aetmp.getLValue()) = globalValueNumber(fc.getArgument(0)) + ) and + globalValueNumber(aetmp.getRValue()) = globalValueNumber(fc) + ) and + msg = "Maybe you're using the function's return value incorrectly." + ) +select fc, msg diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousUseMbtowc.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousUseMbtowc.expected new file mode 100644 index 00000000000..463515526da --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousUseMbtowc.expected @@ -0,0 +1,8 @@ +| test.cpp:61:27:61:32 | call to mbtowc | Size can be less than maximum character length, use macro MB_CUR_MAX. | +| test.cpp:70:27:70:32 | call to mbtowc | Size can be less than maximum character length, use macro MB_CUR_MAX. | +| test.cpp:98:11:98:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer. | +| test.cpp:114:11:114:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer. | +| test.cpp:130:11:130:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer. | +| test.cpp:147:11:147:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer. | +| test.cpp:169:11:169:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer. | +| test.cpp:185:11:185:16 | call to mbtowc | Maybe you're using the function's return value incorrectly. | diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousUseMbtowc.qlref b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousUseMbtowc.qlref new file mode 100644 index 00000000000..f8b0cddbb28 --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousUseMbtowc.qlref @@ -0,0 +1 @@ +experimental/Security/CWE/CWE-125/DangerousUseMbtowc.ql diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test.cpp new file mode 100644 index 00000000000..3945f46103e --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test.cpp @@ -0,0 +1,194 @@ +typedef unsigned long size_t; +#define MB_CUR_MAX 6 +#define MB_LEN_MAX 16 +int mbtowc(wchar_t *out, const char *in, size_t size); +int wprintf (const wchar_t* format, ...); +int strlen( const char * string ); +int checkErrors(); + +void goodTest0() +{ + char * ptr = "123456789"; + int ret; + int len; + len = 9; + for (wchar_t wc; (ret = mbtowc(&wc, ptr, len)) > 0; len-=ret) { // GOOD + wprintf(L"%lc", wc); + } +} +void goodTest1(const char* ptr) +{ + int ret; + int len; + len = strlen(ptr); + for (wchar_t wc; (ret = mbtowc(&wc, ptr, len)) > 0; len-=ret) { // GOOD + wprintf(L"%lc", wc); + } +} +void goodTest2(char* ptr) +{ + int ret; + ptr[10]=0; + int len = 9; + for (wchar_t wc; (ret = mbtowc(&wc, ptr, 16)) > 0; len-=ret) { // GOOD + wprintf(L"%lc", wc); + } +} + +void goodTest3(const char* ptr) +{ + int ret; + int len; + len = strlen(ptr); + for (wchar_t wc; (ret = mbtowc(&wc, ptr, MB_CUR_MAX)) > 0; len-=ret) { // GOOD + wprintf(L"%lc", wc); + } +} +void goodTest4(const char* ptr) +{ + int ret; + int len; + len = strlen(ptr); + for (wchar_t wc; (ret = mbtowc(&wc, ptr, MB_LEN_MAX)) > 0; len-=ret) { // GOOD + wprintf(L"%lc", wc); + } +} +void badTest1(const char* ptr) +{ + int ret; + int len; + len = strlen(ptr); + for (wchar_t wc; (ret = mbtowc(&wc, ptr, 4)) > 0; len-=ret) { // BAD:we can get unpredictable results + wprintf(L"%lc", wc); + } +} +void badTest2(const char* ptr) +{ + int ret; + int len; + len = strlen(ptr); + for (wchar_t wc; (ret = mbtowc(&wc, ptr, sizeof(wchar_t))) > 0; len-=ret) { // BAD:we can get unpredictable results + wprintf(L"%lc", wc); + } +} + +void goodTest5(const char* ptr,wchar_t *wc,int wc_len) +{ + int ret; + int len; + len = wc_len; + while (*ptr && len > 0) { + ret = mbtowc(wc, ptr, len); // GOOD + if (ret <0) + break; + if (ret == 0 || ret > len) + break; + len-=ret; + ptr+=ret; + wc++; + } +} + +void badTest3(const char* ptr,wchar_t *wc,int wc_len) +{ + int ret; + int len; + len = wc_len; + while (*ptr && len > 0) { + ret = mbtowc(wc, ptr, MB_CUR_MAX); // BAD + if (ret <0) + break; + if (ret == 0 || ret > len) + break; + len-=ret; + ptr+=ret; + wc++; + } +} +void badTest4(const char* ptr,wchar_t *wc,int wc_len) +{ + int ret; + int len; + len = wc_len; + while (*ptr && len > 0) { + ret = mbtowc(wc, ptr, 16); // BAD + if (ret <0) + break; + if (ret == 0 || ret > len) + break; + len-=ret; + ptr+=ret; + wc++; + } +} +void badTest5(const char* ptr,wchar_t *wc,int wc_len) +{ + int ret; + int len; + len = wc_len; + while (*ptr && len > 0) { + ret = mbtowc(wc, ptr, sizeof(wchar_t)); // BAD + if (ret <0) + break; + if (ret == 0 || ret > len) + break; + len-=ret; + ptr+=ret; + wc++; + } +} + +void badTest6(const char* ptr,wchar_t *wc,int wc_len) +{ + int ret; + int len; + len = wc_len; + while (*ptr && wc_len > 0) { + ret = mbtowc(wc, ptr, wc_len); // BAD + if (ret <0) + if (checkErrors()) { + ++ptr; + --len; + continue; + } else + break; + if (ret == 0 || ret > len) + break; + wc_len--; + len-=ret; + wc++; + ptr+=ret; + } +} +void badTest7(const char* ptr,wchar_t *wc,int wc_len) +{ + int ret; + int len; + len = wc_len; + while (*ptr && wc_len > 0) { + ret = mbtowc(wc, ptr, len); // BAD + if (ret <0) + break; + if (ret == 0 || ret > len) + break; + len--; + wc++; + ptr+=ret; + } +} +void badTest8(const char* ptr,wchar_t *wc) +{ + int ret; + int len; + len = strlen(ptr); + while (*ptr && len > 0) { + ret = mbtowc(wc, ptr, len); // BAD + if (ret <0) + break; + if (ret == 0 || ret > len) + break; + len-=ret; + ptr++; + wc+=ret; + } +} From 57127a534330b67712a997f07d8c48ee8ef7a154 Mon Sep 17 00:00:00 2001 From: ihsinme Date: Wed, 25 May 2022 09:38:02 +0300 Subject: [PATCH 014/505] Update cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.qhelp Co-authored-by: Geoffrey White <40627776+geoffw0@users.noreply.github.com> --- .../experimental/Security/CWE/CWE-125/DangerousUseMbtowc.qhelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.qhelp index 8634b9b66ba..5a9aa77214d 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.qhelp +++ b/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.qhelp @@ -3,7 +3,7 @@ "qhelp.dtd"> -

    Using function mbtowc with an invalid length argument can result in an out-of-bounds access error or unexpected result. If you are sure you are working with a null-terminated string, use the length macros, if not, use the correctly computed length.

    +

    Using function mbtowc with an invalid length argument can result in an out-of-bounds access error or unexpected result. If you are sure you are working with a null-terminated string, use the length macros, if not, use the correctly computed length.

    From 85fab200867f1adf87f0102452be5bc96aa48774 Mon Sep 17 00:00:00 2001 From: Tony Torralba Date: Thu, 12 May 2022 12:49:02 +0200 Subject: [PATCH 015/505] Add Expr::getUnderlyingExpr predicate --- .../change-notes/2022-05-12-get-underlying-expr.md | 4 ++++ java/ql/lib/semmle/code/java/Expr.qll | 12 ++++++++++++ .../security/CleartextStorageSharedPrefsQuery.qll | 4 ++-- .../code/java/security/UnsafeAndroidAccess.qll | 5 ++++- 4 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 java/ql/lib/change-notes/2022-05-12-get-underlying-expr.md diff --git a/java/ql/lib/change-notes/2022-05-12-get-underlying-expr.md b/java/ql/lib/change-notes/2022-05-12-get-underlying-expr.md new file mode 100644 index 00000000000..f24c9379abb --- /dev/null +++ b/java/ql/lib/change-notes/2022-05-12-get-underlying-expr.md @@ -0,0 +1,4 @@ +--- +category: feature +--- +* The QL predicate `Expr::getUnderlyingExpr` has been added. It can be used to look through casts and not-null expressions and obtain the underlying expression to which they apply. diff --git a/java/ql/lib/semmle/code/java/Expr.qll b/java/ql/lib/semmle/code/java/Expr.qll index 48023e135af..2c969a8d442 100755 --- a/java/ql/lib/semmle/code/java/Expr.qll +++ b/java/ql/lib/semmle/code/java/Expr.qll @@ -100,6 +100,18 @@ class Expr extends ExprParent, @expr { /** Holds if this expression is parenthesized. */ predicate isParenthesized() { isParenthesized(this, _) } + + /** + * Gets the underlying expression looking through casts and not-nulls, if any. + * Otherwise just gets this expression. + */ + Expr getUnderlyingExpr() { + if this instanceof CastingExpr or this instanceof NotNullExpr + then + result = this.(CastingExpr).getExpr().getUnderlyingExpr() or + result = this.(NotNullExpr).getExpr().getUnderlyingExpr() + else result = this + } } /** diff --git a/java/ql/lib/semmle/code/java/security/CleartextStorageSharedPrefsQuery.qll b/java/ql/lib/semmle/code/java/security/CleartextStorageSharedPrefsQuery.qll index 30a8ffc3a12..e67a2d1aa21 100644 --- a/java/ql/lib/semmle/code/java/security/CleartextStorageSharedPrefsQuery.qll +++ b/java/ql/lib/semmle/code/java/security/CleartextStorageSharedPrefsQuery.qll @@ -51,7 +51,7 @@ private predicate sharedPreferencesInput(DataFlow::Node editor, Expr input) { exists(MethodAccess m | m.getMethod() instanceof PutSharedPreferenceMethod and input = m.getArgument(1) and - editor.asExpr() = m.getQualifier() + editor.asExpr() = m.getQualifier().getUnderlyingExpr() ) } @@ -61,7 +61,7 @@ private predicate sharedPreferencesInput(DataFlow::Node editor, Expr input) { */ private predicate sharedPreferencesStore(DataFlow::Node editor, MethodAccess m) { m.getMethod() instanceof StoreSharedPreferenceMethod and - editor.asExpr() = m.getQualifier() + editor.asExpr() = m.getQualifier().getUnderlyingExpr() } /** Flow from `SharedPreferences.Editor` to either a setter or a store method. */ diff --git a/java/ql/lib/semmle/code/java/security/UnsafeAndroidAccess.qll b/java/ql/lib/semmle/code/java/security/UnsafeAndroidAccess.qll index 176b093b68a..ba162ede986 100644 --- a/java/ql/lib/semmle/code/java/security/UnsafeAndroidAccess.qll +++ b/java/ql/lib/semmle/code/java/security/UnsafeAndroidAccess.qll @@ -75,6 +75,8 @@ private predicate webViewLoadUrl(Argument urlArg, WebViewRef webview) { loadUrl.getArgument(0) = urlArg and loadUrl.getMethod() instanceof WebViewLoadUrlMethod | + webview.getAnAccess() = DataFlow::exprNode(loadUrl.getQualifier().getUnderlyingExpr()) + or webview.getAnAccess() = DataFlow::getInstanceArgument(loadUrl) or // `webview` is received as a parameter of an event method in a custom `WebViewClient`, @@ -82,8 +84,9 @@ private predicate webViewLoadUrl(Argument urlArg, WebViewRef webview) { exists(WebViewClientEventMethod eventMethod, MethodAccess setWebClient | setWebClient.getMethod() instanceof WebViewSetWebViewClientMethod and setWebClient.getArgument(0).getType() = eventMethod.getDeclaringType() and - loadUrl.getQualifier() = eventMethod.getWebViewParameter().getAnAccess() + loadUrl.getQualifier().getUnderlyingExpr() = eventMethod.getWebViewParameter().getAnAccess() | + webview.getAnAccess() = DataFlow::exprNode(setWebClient.getQualifier().getUnderlyingExpr()) or webview.getAnAccess() = DataFlow::getInstanceArgument(setWebClient) ) ) From f0b90b391f2ed866cda8df311835b8a06051d869 Mon Sep 17 00:00:00 2001 From: Tony Torralba Date: Fri, 13 May 2022 17:55:55 +0200 Subject: [PATCH 016/505] Add Kotlin test for CleartextStorageSharedPrefs --- .../CWE-312/CleartextStorageSharedPrefsTestKt.kt | 11 +++++++++++ java/ql/test/query-tests/security/CWE-312/options | 1 + 2 files changed, 12 insertions(+) create mode 100644 java/ql/test/query-tests/security/CWE-312/CleartextStorageSharedPrefsTestKt.kt diff --git a/java/ql/test/query-tests/security/CWE-312/CleartextStorageSharedPrefsTestKt.kt b/java/ql/test/query-tests/security/CWE-312/CleartextStorageSharedPrefsTestKt.kt new file mode 100644 index 00000000000..f2f18eaeb0a --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-312/CleartextStorageSharedPrefsTestKt.kt @@ -0,0 +1,11 @@ +import android.app.Activity +import android.content.Context +import android.content.SharedPreferences + +class CleartextStorageSharedPrefsTestKt : Activity() { + fun testSetSharedPrefs1(context: Context, name: String, password: String) { + val sharedPrefs = context.getSharedPreferences("user_prefs", Context.MODE_PRIVATE); + sharedPrefs.edit().putString("name", name).apply(); // Safe + sharedPrefs.edit().putString("password", password).apply(); // $ hasCleartextStorageSharedPrefs + } +} diff --git a/java/ql/test/query-tests/security/CWE-312/options b/java/ql/test/query-tests/security/CWE-312/options index f017f81ff2f..93845c4b2a8 100644 --- a/java/ql/test/query-tests/security/CWE-312/options +++ b/java/ql/test/query-tests/security/CWE-312/options @@ -1 +1,2 @@ // semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/google-android-9.0.0 +// codeql-extractor-kotlin-options: ${testdir}/../../../stubs/google-android-9.0.0 From 9c941dc7ab54249ca88cf66fdbdcf56daec3b0f6 Mon Sep 17 00:00:00 2001 From: Tony Torralba Date: Tue, 24 May 2022 10:51:31 +0200 Subject: [PATCH 017/505] Add Kotlin test for UnsafeAndroidAccess --- .../security/CWE-749/AndroidManifest.xml | 1 + .../security/CWE-749/UnsafeActivityKt.kt | 20 +++++++++++++++++++ .../test/query-tests/security/CWE-749/options | 3 ++- 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 java/ql/test/query-tests/security/CWE-749/UnsafeActivityKt.kt diff --git a/java/ql/test/query-tests/security/CWE-749/AndroidManifest.xml b/java/ql/test/query-tests/security/CWE-749/AndroidManifest.xml index b215e4d3466..79d79d0cd10 100755 --- a/java/ql/test/query-tests/security/CWE-749/AndroidManifest.xml +++ b/java/ql/test/query-tests/security/CWE-749/AndroidManifest.xml @@ -44,6 +44,7 @@ + diff --git a/java/ql/test/query-tests/security/CWE-749/UnsafeActivityKt.kt b/java/ql/test/query-tests/security/CWE-749/UnsafeActivityKt.kt new file mode 100644 index 00000000000..d20845f5c77 --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-749/UnsafeActivityKt.kt @@ -0,0 +1,20 @@ +package com.example.app + +import android.app.Activity +import android.os.Bundle +import android.webkit.WebSettings +import android.webkit.WebView +import android.webkit.WebViewClient + +class UnsafeActivityKt : Activity() { + override fun onCreate(savedInstanceState : Bundle) { + + val wv = findViewById(-1) + // Implicit not-nulls happening here + wv.settings.setJavaScriptEnabled(true) + wv.settings.setAllowFileAccessFromFileURLs(true) + + val thisUrl : String = intent.extras.getString("url") + wv.loadUrl(thisUrl) // $ hasUnsafeAndroidAccess + } +} diff --git a/java/ql/test/query-tests/security/CWE-749/options b/java/ql/test/query-tests/security/CWE-749/options index d6a9adcece3..49f527db1db 100644 --- a/java/ql/test/query-tests/security/CWE-749/options +++ b/java/ql/test/query-tests/security/CWE-749/options @@ -1 +1,2 @@ -//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/android +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/google-android-9.0.0 +//codeql-extractor-kotlin-options: ${testdir}/../../../stubs/google-android-9.0.0 From 872dd0d59f8a34c39825d05d743bd9a445240365 Mon Sep 17 00:00:00 2001 From: ihsinme Date: Thu, 2 Jun 2022 14:33:06 +0300 Subject: [PATCH 018/505] Update DangerousUseMbtowc.expected --- .../semmle/tests/DangerousUseMbtowc.expected | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousUseMbtowc.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousUseMbtowc.expected index 463515526da..89feb7bf82e 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousUseMbtowc.expected +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousUseMbtowc.expected @@ -1,8 +1,8 @@ -| test.cpp:61:27:61:32 | call to mbtowc | Size can be less than maximum character length, use macro MB_CUR_MAX. | -| test.cpp:70:27:70:32 | call to mbtowc | Size can be less than maximum character length, use macro MB_CUR_MAX. | -| test.cpp:98:11:98:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer. | -| test.cpp:114:11:114:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer. | -| test.cpp:130:11:130:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer. | -| test.cpp:147:11:147:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer. | -| test.cpp:169:11:169:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer. | -| test.cpp:185:11:185:16 | call to mbtowc | Maybe you're using the function's return value incorrectly. | +| test.cpp:66:27:66:32 | call to mbtowc | Size can be less than maximum character length, use macro MB_CUR_MAX. | +| test.cpp:76:27:76:32 | call to mbtowc | Size can be less than maximum character length, use macro MB_CUR_MAX. | +| test.cpp:106:11:106:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer. | +| test.cpp:123:11:123:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer. | +| test.cpp:140:11:140:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer. | +| test.cpp:158:11:158:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer. | +| test.cpp:181:11:181:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer. | +| test.cpp:197:11:197:16 | call to mbtowc | Maybe you're using the function's return value incorrectly. | From 77e4d05ea344a131a7d7a39061056db62dcd962a Mon Sep 17 00:00:00 2001 From: ihsinme Date: Thu, 2 Jun 2022 14:33:59 +0300 Subject: [PATCH 019/505] Update test.cpp --- .../CWE/CWE-125/semmle/tests/test.cpp | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test.cpp index 3945f46103e..b5e8096af2a 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test.cpp +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test.cpp @@ -14,6 +14,7 @@ void goodTest0() len = 9; for (wchar_t wc; (ret = mbtowc(&wc, ptr, len)) > 0; len-=ret) { // GOOD wprintf(L"%lc", wc); + ptr += ret; } } void goodTest1(const char* ptr) @@ -23,6 +24,7 @@ void goodTest1(const char* ptr) len = strlen(ptr); for (wchar_t wc; (ret = mbtowc(&wc, ptr, len)) > 0; len-=ret) { // GOOD wprintf(L"%lc", wc); + ptr += ret; } } void goodTest2(char* ptr) @@ -32,6 +34,7 @@ void goodTest2(char* ptr) int len = 9; for (wchar_t wc; (ret = mbtowc(&wc, ptr, 16)) > 0; len-=ret) { // GOOD wprintf(L"%lc", wc); + ptr += ret; } } @@ -42,6 +45,7 @@ void goodTest3(const char* ptr) len = strlen(ptr); for (wchar_t wc; (ret = mbtowc(&wc, ptr, MB_CUR_MAX)) > 0; len-=ret) { // GOOD wprintf(L"%lc", wc); + ptr += ret; } } void goodTest4(const char* ptr) @@ -51,6 +55,7 @@ void goodTest4(const char* ptr) len = strlen(ptr); for (wchar_t wc; (ret = mbtowc(&wc, ptr, MB_LEN_MAX)) > 0; len-=ret) { // GOOD wprintf(L"%lc", wc); + ptr += ret; } } void badTest1(const char* ptr) @@ -60,6 +65,7 @@ void badTest1(const char* ptr) len = strlen(ptr); for (wchar_t wc; (ret = mbtowc(&wc, ptr, 4)) > 0; len-=ret) { // BAD:we can get unpredictable results wprintf(L"%lc", wc); + ptr += ret; } } void badTest2(const char* ptr) @@ -69,6 +75,7 @@ void badTest2(const char* ptr) len = strlen(ptr); for (wchar_t wc; (ret = mbtowc(&wc, ptr, sizeof(wchar_t))) > 0; len-=ret) { // BAD:we can get unpredictable results wprintf(L"%lc", wc); + ptr += ret; } } @@ -89,11 +96,12 @@ void goodTest5(const char* ptr,wchar_t *wc,int wc_len) } } -void badTest3(const char* ptr,wchar_t *wc,int wc_len) +void badTest3(const char* ptr,int wc_len) { int ret; int len; len = wc_len; + wchar_t *wc = new wchar_t[wc_len]; while (*ptr && len > 0) { ret = mbtowc(wc, ptr, MB_CUR_MAX); // BAD if (ret <0) @@ -105,11 +113,12 @@ void badTest3(const char* ptr,wchar_t *wc,int wc_len) wc++; } } -void badTest4(const char* ptr,wchar_t *wc,int wc_len) +void badTest4(const char* ptr,int wc_len) { int ret; int len; len = wc_len; + wchar_t *wc = new wchar_t[wc_len]; while (*ptr && len > 0) { ret = mbtowc(wc, ptr, 16); // BAD if (ret <0) @@ -121,11 +130,12 @@ void badTest4(const char* ptr,wchar_t *wc,int wc_len) wc++; } } -void badTest5(const char* ptr,wchar_t *wc,int wc_len) +void badTest5(const char* ptr,int wc_len) { int ret; int len; len = wc_len; + wchar_t *wc = new wchar_t[wc_len]; while (*ptr && len > 0) { ret = mbtowc(wc, ptr, sizeof(wchar_t)); // BAD if (ret <0) @@ -138,11 +148,12 @@ void badTest5(const char* ptr,wchar_t *wc,int wc_len) } } -void badTest6(const char* ptr,wchar_t *wc,int wc_len) +void badTest6(const char* ptr,int wc_len) { int ret; int len; len = wc_len; + wchar_t *wc = new wchar_t[wc_len]; while (*ptr && wc_len > 0) { ret = mbtowc(wc, ptr, wc_len); // BAD if (ret <0) @@ -160,11 +171,12 @@ void badTest6(const char* ptr,wchar_t *wc,int wc_len) ptr+=ret; } } -void badTest7(const char* ptr,wchar_t *wc,int wc_len) +void badTest7(const char* ptr,int wc_len) { int ret; int len; len = wc_len; + wchar_t *wc = new wchar_t[wc_len]; while (*ptr && wc_len > 0) { ret = mbtowc(wc, ptr, len); // BAD if (ret <0) From 9d12f1be53c4f1a5a7d42d2e384e91fbe8509002 Mon Sep 17 00:00:00 2001 From: ihsinme Date: Thu, 2 Jun 2022 14:34:38 +0300 Subject: [PATCH 020/505] Update DangerousUseMbtowc.ql --- .../experimental/Security/CWE/CWE-125/DangerousUseMbtowc.ql | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.ql b/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.ql index 0940c899312..2b668025794 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.ql @@ -19,7 +19,10 @@ predicate exprMayBeString(Expr exp) { exists(StringLiteral sl | globalValueNumber(exp) = globalValueNumber(sl)) or exists(FunctionCall fctmp | - globalValueNumber(fctmp.getAnArgument()) = globalValueNumber(exp) and + ( + fctmp.getAnArgument().(VariableAccess).getTarget() = exp.(VariableAccess).getTarget() or + globalValueNumber(fctmp.getAnArgument()) = globalValueNumber(exp) + ) and fctmp.getTarget().hasGlobalOrStdName(["strlen", "strcat", "strncat", "strcpy", "sptintf"]) ) or From c012c235c6efb1628ed75c7fb3d7594ddd51f544 Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Tue, 14 Jun 2022 01:06:39 +0000 Subject: [PATCH 021/505] rough draft of check request verb query --- .../ManuallyCheckHttpVerb.ql | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql diff --git a/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql b/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql new file mode 100644 index 00000000000..ab71ba45d46 --- /dev/null +++ b/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql @@ -0,0 +1,78 @@ +/** + * @name Manually checking http verb instead of using built in rails routes and protections + * @description Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. + * @kind problem + * @problem.severity error + * @security-severity 7.0 + * @precision medium + * @id rb/manually-checking-http-verb + * @tags security + */ + +import ruby +import codeql.ruby.DataFlow + +class ManuallyCheckHttpVerb extends DataFlow::CallNode { + ManuallyCheckHttpVerb() { + this instanceof CheckGetRequest or + this instanceof CheckPostRequest or + this instanceof CheckPatchRequest or + this instanceof CheckPostRequest or + this instanceof CheckDeleteRequest or + this instanceof CheckHeadRequest or + this.asExpr().getExpr() instanceof CheckRequestMethodFromEnv + } +} + +class CheckRequestMethodFromEnv extends ComparisonOperation { + CheckRequestMethodFromEnv() { + this.getAnOperand() instanceof GetRequestMethodFromEnv + } +} + +class GetRequestMethodFromEnv extends ElementReference { + GetRequestMethodFromEnv() { + this.getAChild+().toString() = "REQUEST_METHOD" // and + // this.getReceiver().toString() = "env" + } +} + +class CheckGetRequest extends DataFlow::CallNode { + CheckGetRequest() { + this.getMethodName() = "get?" + } +} + +class CheckPostRequest extends DataFlow::CallNode { + CheckPostRequest() { + this.getMethodName() = "post?" + } +} + +class CheckPutRequest extends DataFlow::CallNode { + CheckPutRequest() { + this.getMethodName() = "put?" + } +} + +class CheckPatchRequest extends DataFlow::CallNode { + CheckPatchRequest() { + this.getMethodName() = "patch?" + } +} + +class CheckDeleteRequest extends DataFlow::CallNode { + CheckDeleteRequest() { + this.getMethodName() = "delete?" + } +} + +class CheckHeadRequest extends DataFlow::CallNode { + CheckHeadRequest() { + this.getMethodName() = "head?" + } +} + +from ManuallyCheckHttpVerb check +where check.asExpr().getExpr().getAControlFlowNode().isCondition() +select check, "Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods." \ No newline at end of file From 7bdec98e6f73764028038476dc8c2c14d51e99f5 Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Tue, 14 Jun 2022 02:13:15 +0000 Subject: [PATCH 022/505] draft tests --- .../ExampleController.rb | 51 +++++++++++++++++++ .../ManuallyCheckHttpVerb.qlref | 1 + .../manually-check-http-verb/NotController.rb | 28 ++++++++++ 3 files changed, 80 insertions(+) create mode 100644 ruby/ql/test/query-tests/security/manually-check-http-verb/ExampleController.rb create mode 100644 ruby/ql/test/query-tests/security/manually-check-http-verb/ManuallyCheckHttpVerb.qlref create mode 100644 ruby/ql/test/query-tests/security/manually-check-http-verb/NotController.rb diff --git a/ruby/ql/test/query-tests/security/manually-check-http-verb/ExampleController.rb b/ruby/ql/test/query-tests/security/manually-check-http-verb/ExampleController.rb new file mode 100644 index 00000000000..a67d4e5306a --- /dev/null +++ b/ruby/ql/test/query-tests/security/manually-check-http-verb/ExampleController.rb @@ -0,0 +1,51 @@ +class ExampleController < ActionController::Base + # This function should have 6 vulnerable lines + def example_action + if request.get? + Example.find(params[:example_id]) + elsif request.post? + Example.new(params[:example_name], params[:example_details]) + elsif request.delete? + example = Example.find(params[:example_id]) + example.delete + elsif request.put? + Example.upsert(params[:example_name], params[:example_details]) + elsif request.path? + Example.update(params[:example_name], params[:example_details]) + elsif request.head? + "This is the endpoint for the Example resource." + end + end +end + +class ResourceController < ActionController::Base + # This method should have 1 vulnerable line + def resource_action + case env['REQUEST_METHOD'] + when "GET" + Resource.find(params[:id]) + when "POST" + Resource.new(params[:id], params[:details]) + end + end +end + +class SafeController < ActionController::Base + # this method should have no hits because controllers rely on conventional Rails routes + def index + Safe.find(params[:id]) + end + + def create + Safe.new(params[:id], params[:details]) + end + + def update + Safe.update(params[:id], params[:details]) + end + + def delete + s = Safe.find(params[:id]) + s.delete + end +end \ No newline at end of file diff --git a/ruby/ql/test/query-tests/security/manually-check-http-verb/ManuallyCheckHttpVerb.qlref b/ruby/ql/test/query-tests/security/manually-check-http-verb/ManuallyCheckHttpVerb.qlref new file mode 100644 index 00000000000..463c21cd0f2 --- /dev/null +++ b/ruby/ql/test/query-tests/security/manually-check-http-verb/ManuallyCheckHttpVerb.qlref @@ -0,0 +1 @@ +experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql \ No newline at end of file diff --git a/ruby/ql/test/query-tests/security/manually-check-http-verb/NotController.rb b/ruby/ql/test/query-tests/security/manually-check-http-verb/NotController.rb new file mode 100644 index 00000000000..81a73d72410 --- /dev/null +++ b/ruby/ql/test/query-tests/security/manually-check-http-verb/NotController.rb @@ -0,0 +1,28 @@ +# There should be no hits from this class because it does not inherit from ActionController +class NotAController + def example_action + if request.get? + Example.find(params[:example_id]) + elsif request.post? + Example.new(params[:example_name], params[:example_details]) + elsif request.delete? + example = Example.find(params[:example_id]) + example.delete + elsif request.put? + Example.upsert(params[:example_name], params[:example_details]) + elsif request.path? + Example.update(params[:example_name], params[:example_details]) + elsif request.head? + "This is the endpoint for the Example resource." + end + end + + def resource_action + case env['REQUEST_METHOD'] + when "GET" + Resource.find(params[:id]) + when "POST" + Resource.new(params[:id], params[:details]) + end + end +end \ No newline at end of file From 6bef71ea2ca8960082507da96b1967265fb3f943 Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Tue, 14 Jun 2022 02:17:12 +0000 Subject: [PATCH 023/505] tweaks to tests --- .../manually-check-http-verb/ExampleController.rb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ruby/ql/test/query-tests/security/manually-check-http-verb/ExampleController.rb b/ruby/ql/test/query-tests/security/manually-check-http-verb/ExampleController.rb index a67d4e5306a..c3e913367b8 100644 --- a/ruby/ql/test/query-tests/security/manually-check-http-verb/ExampleController.rb +++ b/ruby/ql/test/query-tests/security/manually-check-http-verb/ExampleController.rb @@ -18,8 +18,16 @@ class ExampleController < ActionController::Base end end +class OtherController < ActionController::Base + def other_action + if env['REQUEST_METHOD'] == "GET" + Other.find(params[:id]) + end + end +end + class ResourceController < ActionController::Base - # This method should have 1 vulnerable line + # This method should have 1 vulnerable line, but is currently failing because it's not a comparison node def resource_action case env['REQUEST_METHOD'] when "GET" From 92d1c84f051e35fb4006244088852ca519bf5066 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 14 Jun 2022 13:22:09 +0200 Subject: [PATCH 024/505] bind the result in JsonValue::getBooleanValue --- javascript/ql/lib/semmle/javascript/JSON.qll | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/javascript/ql/lib/semmle/javascript/JSON.qll b/javascript/ql/lib/semmle/javascript/JSON.qll index d474965051e..8230099818c 100644 --- a/javascript/ql/lib/semmle/javascript/JSON.qll +++ b/javascript/ql/lib/semmle/javascript/JSON.qll @@ -54,7 +54,9 @@ class JsonValue extends @json_value, Locatable { int getIntValue() { result = this.(JsonNumber).getValue().toInt() } /** If this is a boolean constant, gets its boolean value. */ - boolean getBooleanValue() { result.toString() = this.(JsonBoolean).getValue() } + boolean getBooleanValue() { + result.toString() = this.(JsonBoolean).getValue() and result = [true, false] + } override string getAPrimaryQlClass() { result = "JsonValue" } } From cb0a6936adb154a1c1361154d9bf36b7a5d16bfb Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 14 Jun 2022 13:31:47 +0200 Subject: [PATCH 025/505] add support for the "exports" property in a package.json --- .../javascript/NodeModuleResolutionImpl.qll | 9 ++++++++- .../ReDoS/PolynomialBackTracking.expected | 2 ++ .../Performance/ReDoS/PolynomialReDoS.expected | 18 ++++++++++++++++++ .../Performance/ReDoS/lib/subLib5/feature.js | 3 +++ .../Performance/ReDoS/lib/subLib5/main.js | 3 +++ .../Performance/ReDoS/lib/subLib5/package.json | 11 +++++++++++ 6 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 javascript/ql/test/query-tests/Performance/ReDoS/lib/subLib5/feature.js create mode 100644 javascript/ql/test/query-tests/Performance/ReDoS/lib/subLib5/main.js create mode 100644 javascript/ql/test/query-tests/Performance/ReDoS/lib/subLib5/package.json diff --git a/javascript/ql/lib/semmle/javascript/NodeModuleResolutionImpl.qll b/javascript/ql/lib/semmle/javascript/NodeModuleResolutionImpl.qll index 70f27ccb12d..e264a78f230 100644 --- a/javascript/ql/lib/semmle/javascript/NodeModuleResolutionImpl.qll +++ b/javascript/ql/lib/semmle/javascript/NodeModuleResolutionImpl.qll @@ -148,7 +148,11 @@ private string getASrcFolderName() { result = ["ts", "js", "src", "lib"] } class MainModulePath extends PathExpr, @json_string { PackageJson pkg; - MainModulePath() { this = pkg.getPropValue(["main", "module"]) } + MainModulePath() { + this = pkg.getPropValue(["main", "module"]) + or + this = getAJsonChild*(pkg.getPropValue("exports")) + } /** Gets the `package.json` file in which this path occurs. */ PackageJson getPackageJson() { result = pkg } @@ -164,6 +168,9 @@ class MainModulePath extends PathExpr, @json_string { } } +/** Gets the value of a property from the JSON object `obj`. */ +private JsonValue getAJsonChild(JsonObject obj) { result = obj.getPropValue(_) } + module MainModulePath { MainModulePath of(PackageJson pkg) { result.getPackageJson() = pkg } } diff --git a/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialBackTracking.expected b/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialBackTracking.expected index 96919f8717d..7df8bece899 100644 --- a/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialBackTracking.expected +++ b/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialBackTracking.expected @@ -38,6 +38,8 @@ | lib/snapdragon.js:15:26:15:27 | a* | Strings starting with 'a' and with many repetitions of 'a' can start matching anywhere after the start of the preceeding aa*$ | | lib/snapdragon.js:23:22:23:23 | a* | Strings starting with 'a' and with many repetitions of 'a' can start matching anywhere after the start of the preceeding aa*$ | | lib/subLib4/factory.js:8:3:8:4 | f* | Strings with many repetitions of 'f' can start matching anywhere after the start of the preceeding f*g | +| lib/subLib5/feature.js:2:3:2:4 | a* | Strings with many repetitions of 'a' can start matching anywhere after the start of the preceeding a*b | +| lib/subLib5/main.js:2:3:2:4 | a* | Strings with many repetitions of 'a' can start matching anywhere after the start of the preceeding a*b | | lib/sublib/factory.js:13:14:13:15 | f* | Strings with many repetitions of 'f' can start matching anywhere after the start of the preceeding f*g | | polynomial-redos.js:7:24:7:26 | \\s+ | Strings with many repetitions of ' ' can start matching anywhere after the start of the preceeding \\s+$ | | polynomial-redos.js:8:17:8:18 | * | Strings with many repetitions of ' ' can start matching anywhere after the start of the preceeding *, * | diff --git a/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialReDoS.expected b/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialReDoS.expected index dccfcc03ed3..d03213a37d0 100644 --- a/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialReDoS.expected +++ b/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialReDoS.expected @@ -51,6 +51,14 @@ nodes | lib/subLib4/factory.js:7:27:7:30 | name | | lib/subLib4/factory.js:8:13:8:16 | name | | lib/subLib4/factory.js:8:13:8:16 | name | +| lib/subLib5/feature.js:1:28:1:31 | name | +| lib/subLib5/feature.js:1:28:1:31 | name | +| lib/subLib5/feature.js:2:13:2:16 | name | +| lib/subLib5/feature.js:2:13:2:16 | name | +| lib/subLib5/main.js:1:28:1:31 | name | +| lib/subLib5/main.js:1:28:1:31 | name | +| lib/subLib5/main.js:2:13:2:16 | name | +| lib/subLib5/main.js:2:13:2:16 | name | | lib/sublib/factory.js:12:26:12:29 | name | | lib/sublib/factory.js:12:26:12:29 | name | | lib/sublib/factory.js:13:24:13:27 | name | @@ -256,6 +264,14 @@ edges | lib/subLib4/factory.js:7:27:7:30 | name | lib/subLib4/factory.js:8:13:8:16 | name | | lib/subLib4/factory.js:7:27:7:30 | name | lib/subLib4/factory.js:8:13:8:16 | name | | lib/subLib4/factory.js:7:27:7:30 | name | lib/subLib4/factory.js:8:13:8:16 | name | +| lib/subLib5/feature.js:1:28:1:31 | name | lib/subLib5/feature.js:2:13:2:16 | name | +| lib/subLib5/feature.js:1:28:1:31 | name | lib/subLib5/feature.js:2:13:2:16 | name | +| lib/subLib5/feature.js:1:28:1:31 | name | lib/subLib5/feature.js:2:13:2:16 | name | +| lib/subLib5/feature.js:1:28:1:31 | name | lib/subLib5/feature.js:2:13:2:16 | name | +| lib/subLib5/main.js:1:28:1:31 | name | lib/subLib5/main.js:2:13:2:16 | name | +| lib/subLib5/main.js:1:28:1:31 | name | lib/subLib5/main.js:2:13:2:16 | name | +| lib/subLib5/main.js:1:28:1:31 | name | lib/subLib5/main.js:2:13:2:16 | name | +| lib/subLib5/main.js:1:28:1:31 | name | lib/subLib5/main.js:2:13:2:16 | name | | lib/sublib/factory.js:12:26:12:29 | name | lib/sublib/factory.js:13:24:13:27 | name | | lib/sublib/factory.js:12:26:12:29 | name | lib/sublib/factory.js:13:24:13:27 | name | | lib/sublib/factory.js:12:26:12:29 | name | lib/sublib/factory.js:13:24:13:27 | name | @@ -418,6 +434,8 @@ edges | lib/snapdragon.js:15:13:15:30 | this.match(/aa*$/) | lib/snapdragon.js:12:34:12:38 | input | lib/snapdragon.js:15:13:15:16 | this | This $@ that depends on $@ may run slow on strings starting with 'a' and with many repetitions of 'a'. | lib/snapdragon.js:15:26:15:27 | a* | regular expression | lib/snapdragon.js:12:34:12:38 | input | library input | | lib/snapdragon.js:23:5:23:26 | node.va ... /aa*$/) | lib/snapdragon.js:20:34:20:38 | input | lib/snapdragon.js:23:5:23:12 | node.val | This $@ that depends on $@ may run slow on strings starting with 'a' and with many repetitions of 'a'. | lib/snapdragon.js:23:22:23:23 | a* | regular expression | lib/snapdragon.js:20:34:20:38 | input | library input | | lib/subLib4/factory.js:8:2:8:17 | /f*g/.test(name) | lib/subLib4/factory.js:7:27:7:30 | name | lib/subLib4/factory.js:8:13:8:16 | name | This $@ that depends on $@ may run slow on strings with many repetitions of 'f'. | lib/subLib4/factory.js:8:3:8:4 | f* | regular expression | lib/subLib4/factory.js:7:27:7:30 | name | library input | +| lib/subLib5/feature.js:2:2:2:17 | /a*b/.test(name) | lib/subLib5/feature.js:1:28:1:31 | name | lib/subLib5/feature.js:2:13:2:16 | name | This $@ that depends on $@ may run slow on strings with many repetitions of 'a'. | lib/subLib5/feature.js:2:3:2:4 | a* | regular expression | lib/subLib5/feature.js:1:28:1:31 | name | library input | +| lib/subLib5/main.js:2:2:2:17 | /a*b/.test(name) | lib/subLib5/main.js:1:28:1:31 | name | lib/subLib5/main.js:2:13:2:16 | name | This $@ that depends on $@ may run slow on strings with many repetitions of 'a'. | lib/subLib5/main.js:2:3:2:4 | a* | regular expression | lib/subLib5/main.js:1:28:1:31 | name | library input | | lib/sublib/factory.js:13:13:13:28 | /f*g/.test(name) | lib/sublib/factory.js:12:26:12:29 | name | lib/sublib/factory.js:13:24:13:27 | name | This $@ that depends on $@ may run slow on strings with many repetitions of 'f'. | lib/sublib/factory.js:13:14:13:15 | f* | regular expression | lib/sublib/factory.js:12:26:12:29 | name | library input | | polynomial-redos.js:7:2:7:34 | tainted ... /g, '') | polynomial-redos.js:5:16:5:32 | req.query.tainted | polynomial-redos.js:7:2:7:8 | tainted | This $@ that depends on $@ may run slow on strings with many repetitions of ' '. | polynomial-redos.js:7:24:7:26 | \\s+ | regular expression | polynomial-redos.js:5:16:5:32 | req.query.tainted | a user-provided value | | polynomial-redos.js:8:2:8:23 | tainted ... *, */) | polynomial-redos.js:5:16:5:32 | req.query.tainted | polynomial-redos.js:8:2:8:8 | tainted | This $@ that depends on $@ may run slow on strings with many repetitions of ' '. | polynomial-redos.js:8:17:8:18 | * | regular expression | polynomial-redos.js:5:16:5:32 | req.query.tainted | a user-provided value | diff --git a/javascript/ql/test/query-tests/Performance/ReDoS/lib/subLib5/feature.js b/javascript/ql/test/query-tests/Performance/ReDoS/lib/subLib5/feature.js new file mode 100644 index 00000000000..4326227e86b --- /dev/null +++ b/javascript/ql/test/query-tests/Performance/ReDoS/lib/subLib5/feature.js @@ -0,0 +1,3 @@ +module.exports = function (name) { + /a*b/.test(name); // NOT OK +}; diff --git a/javascript/ql/test/query-tests/Performance/ReDoS/lib/subLib5/main.js b/javascript/ql/test/query-tests/Performance/ReDoS/lib/subLib5/main.js new file mode 100644 index 00000000000..4326227e86b --- /dev/null +++ b/javascript/ql/test/query-tests/Performance/ReDoS/lib/subLib5/main.js @@ -0,0 +1,3 @@ +module.exports = function (name) { + /a*b/.test(name); // NOT OK +}; diff --git a/javascript/ql/test/query-tests/Performance/ReDoS/lib/subLib5/package.json b/javascript/ql/test/query-tests/Performance/ReDoS/lib/subLib5/package.json new file mode 100644 index 00000000000..f10fc5e5788 --- /dev/null +++ b/javascript/ql/test/query-tests/Performance/ReDoS/lib/subLib5/package.json @@ -0,0 +1,11 @@ +{ + "name": "my-sub-lib", + "version": "0.0.7", + "main": "./main.js", + "exports": { + ".": "./main.js", + "./feature": { + "default": "./feature.js" + } + } +} From 304e2926c9ee67b2cff89152482e9ee28e2a4c0c Mon Sep 17 00:00:00 2001 From: Ian Lynagh Date: Tue, 14 Jun 2022 10:56:15 +0100 Subject: [PATCH 026/505] Java: Fix RefType.getAStrictAncestor() in the presence of type hierarchy cycles --- java/ql/lib/semmle/code/java/Type.qll | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/java/ql/lib/semmle/code/java/Type.qll b/java/ql/lib/semmle/code/java/Type.qll index a37f9810c44..323513b6a44 100755 --- a/java/ql/lib/semmle/code/java/Type.qll +++ b/java/ql/lib/semmle/code/java/Type.qll @@ -413,8 +413,12 @@ class RefType extends Type, Annotatable, Modifiable, @reftype { /** Gets a direct or indirect supertype of this type, including itself. */ RefType getAnAncestor() { hasDescendant(result, this) } - /** Gets a direct or indirect supertype of this type, not including itself. */ - RefType getAStrictAncestor() { result = this.getAnAncestor() and result != this } + /** + * Gets a direct or indirect supertype of this type. + * This does not including itself, unless this type is part of a cycle + * in the type hierarchy. + */ + RefType getAStrictAncestor() { result = this.getASupertype().getAnAncestor() } /** * Gets the source declaration of a direct supertype of this type, excluding itself. From b524fb4f3ae369f67dfc91e51fff37f083224e77 Mon Sep 17 00:00:00 2001 From: Ian Lynagh Date: Tue, 14 Jun 2022 10:58:26 +0100 Subject: [PATCH 027/505] Java: Add a test for cycles in the type hierarchy --- java/ql/test/library-tests/types/cycles/Test.java | 2 ++ java/ql/test/library-tests/types/cycles/cycles.expected | 6 ++++++ java/ql/test/library-tests/types/cycles/cycles.ql | 5 +++++ 3 files changed, 13 insertions(+) create mode 100644 java/ql/test/library-tests/types/cycles/Test.java create mode 100644 java/ql/test/library-tests/types/cycles/cycles.expected create mode 100644 java/ql/test/library-tests/types/cycles/cycles.ql diff --git a/java/ql/test/library-tests/types/cycles/Test.java b/java/ql/test/library-tests/types/cycles/Test.java new file mode 100644 index 00000000000..a06540728b9 --- /dev/null +++ b/java/ql/test/library-tests/types/cycles/Test.java @@ -0,0 +1,2 @@ +public class Test { +} diff --git a/java/ql/test/library-tests/types/cycles/cycles.expected b/java/ql/test/library-tests/types/cycles/cycles.expected new file mode 100644 index 00000000000..7c0e79d7252 --- /dev/null +++ b/java/ql/test/library-tests/types/cycles/cycles.expected @@ -0,0 +1,6 @@ +| BiFunction | +| BiFunction | +| Function | +| Function | +| Map | +| Map | diff --git a/java/ql/test/library-tests/types/cycles/cycles.ql b/java/ql/test/library-tests/types/cycles/cycles.ql new file mode 100644 index 00000000000..dd4d6e1f757 --- /dev/null +++ b/java/ql/test/library-tests/types/cycles/cycles.ql @@ -0,0 +1,5 @@ +import java + +from RefType t +where t = t.getAStrictAncestor() +select t.toString() From 24c9aff2fc477a011d6175dedfd4ead005955378 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Wed, 15 Jun 2022 15:58:17 +0200 Subject: [PATCH 028/505] Python: Fix a type-tracking test --- python/ql/test/experimental/dataflow/typetracking/test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/test/experimental/dataflow/typetracking/test.py b/python/ql/test/experimental/dataflow/typetracking/test.py index 4392e3bbf46..407524d949d 100644 --- a/python/ql/test/experimental/dataflow/typetracking/test.py +++ b/python/ql/test/experimental/dataflow/typetracking/test.py @@ -135,7 +135,7 @@ class Bar(Foo): def track_self(self): # $ tracked_self self.meth1() # $ tracked_self super().meth2() - super(Bar, self).foo3() # $ tracked_self + super(Bar, self).meth3() # $ tracked_self # ------------------------------------------------------------------------------ # Tracking of attribute lookup after "long" import chain From b2c8e0fe8d5edca2fc2b473b5e418260b8e4bcb3 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Wed, 15 Jun 2022 15:59:54 +0200 Subject: [PATCH 029/505] Python: Add comment to test --- python/ql/test/experimental/dataflow/typetracking/test.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/python/ql/test/experimental/dataflow/typetracking/test.py b/python/ql/test/experimental/dataflow/typetracking/test.py index 407524d949d..819d85c1d8a 100644 --- a/python/ql/test/experimental/dataflow/typetracking/test.py +++ b/python/ql/test/experimental/dataflow/typetracking/test.py @@ -65,6 +65,9 @@ def to_inner_scope(): also_x = foo() # $ MISSING: tracked print(also_x) # $ MISSING: tracked +# ------------------------------------------------------------------------------ +# Function decorator +# ------------------------------------------------------------------------------ def my_decorator(func): # This part doesn't make any sense in a normal decorator, but just shows how we From 5f32f898d5bb55fa8c61df132419cebb161b370c Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Wed, 15 Jun 2022 16:16:34 +0200 Subject: [PATCH 030/505] Python: API-graphs: test class decorators and subclass A class decorator could change the class definition in any way. In this specific case, it would be better if we allowed the subclass to be found with API graphs still. inspired by https://github.com/django/django/blob/c2250cfb80e27cdf8d098428824da2800a18cadf/tests/auth_tests/test_views.py#L40-L46 --- .../library-tests/ApiGraphs/py3/deftest2.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/python/ql/test/library-tests/ApiGraphs/py3/deftest2.py b/python/ql/test/library-tests/ApiGraphs/py3/deftest2.py index 63234fa11fc..276b5ca3212 100644 --- a/python/ql/test/library-tests/ApiGraphs/py3/deftest2.py +++ b/python/ql/test/library-tests/ApiGraphs/py3/deftest2.py @@ -16,4 +16,19 @@ def internal(): def my_internal_method(self): #$ def=moduleImport("pflask").getMember("views").getMember("View").getASubclass().getMember("my_internal_method") pass - int_instance = IntMyView() #$ use=moduleImport("pflask").getMember("views").getMember("View").getASubclass().getReturn() \ No newline at end of file + int_instance = IntMyView() #$ use=moduleImport("pflask").getMember("views").getMember("View").getASubclass().getReturn() + +# ------------------------------------------------------------------------------ +# Class decorator +# ------------------------------------------------------------------------------ + +def my_class_decorator(cls): + print("dummy decorator") + return cls + +@my_class_decorator +class MyViewWithDecorator(View): #$ use=moduleImport("flask").getMember("views").getMember("View").getASubclass() + pass + +class SubclassFromDecorated(MyViewWithDecorator): #$ MISSING: use=moduleImport("flask").getMember("views").getMember("View").getASubclass().getASubclass() + pass From d6e68258a4398111d56cf486c6656b269379aa4b Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Wed, 15 Jun 2022 17:30:21 +0200 Subject: [PATCH 031/505] Python: API-graphs: allow class decorators in `.getASubclass()` --- .../2022-06-15-class-decorator-api-subclass.md | 4 ++++ python/ql/lib/semmle/python/ApiGraphs.qll | 14 ++++++++++++-- .../test/library-tests/ApiGraphs/py3/deftest2.py | 2 +- 3 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 python/ql/lib/change-notes/2022-06-15-class-decorator-api-subclass.md diff --git a/python/ql/lib/change-notes/2022-06-15-class-decorator-api-subclass.md b/python/ql/lib/change-notes/2022-06-15-class-decorator-api-subclass.md new file mode 100644 index 00000000000..04beefb14b6 --- /dev/null +++ b/python/ql/lib/change-notes/2022-06-15-class-decorator-api-subclass.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Change `.getASubclass()` on `API::Node` so it allows to follow subclasses even if the class has a class decorator. diff --git a/python/ql/lib/semmle/python/ApiGraphs.qll b/python/ql/lib/semmle/python/ApiGraphs.qll index fcb89e5f866..1023347b832 100644 --- a/python/ql/lib/semmle/python/ApiGraphs.qll +++ b/python/ql/lib/semmle/python/ApiGraphs.qll @@ -591,8 +591,18 @@ module API { or // Subclassing a node lbl = Label::subclass() and - exists(DataFlow::Node superclass | pred.flowsTo(superclass) | - ref.asExpr().(PY::ClassExpr).getABase() = superclass.asExpr() + exists(PY::ClassExpr clsExpr, DataFlow::Node superclass | pred.flowsTo(superclass) | + clsExpr.getABase() = superclass.asExpr() and + // Potentially a class decorator could do anything, but we assume they are + // "benign" and let subclasses edges flow through anyway. + // see example in https://github.com/django/django/blob/c2250cfb80e27cdf8d098428824da2800a18cadf/tests/auth_tests/test_views.py#L40-L46 + ( + not exists(clsExpr.getADecorator()) and + ref.asExpr() = clsExpr + or + ref.asExpr() = clsExpr.getADecoratorCall() and + not exists(PY::Call otherDecorator | otherDecorator.getArg(0) = ref.asExpr()) + ) ) or // awaiting diff --git a/python/ql/test/library-tests/ApiGraphs/py3/deftest2.py b/python/ql/test/library-tests/ApiGraphs/py3/deftest2.py index 276b5ca3212..ef15ece04f6 100644 --- a/python/ql/test/library-tests/ApiGraphs/py3/deftest2.py +++ b/python/ql/test/library-tests/ApiGraphs/py3/deftest2.py @@ -30,5 +30,5 @@ def my_class_decorator(cls): class MyViewWithDecorator(View): #$ use=moduleImport("flask").getMember("views").getMember("View").getASubclass() pass -class SubclassFromDecorated(MyViewWithDecorator): #$ MISSING: use=moduleImport("flask").getMember("views").getMember("View").getASubclass().getASubclass() +class SubclassFromDecorated(MyViewWithDecorator): #$ use=moduleImport("flask").getMember("views").getMember("View").getASubclass().getASubclass() pass From b993558987e3e9286e83b397a0e3b6473b6953e1 Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Wed, 15 Jun 2022 10:14:51 -0700 Subject: [PATCH 032/505] Update docs to include how to run a pack with path `scope/name@range:path` is a valid way to specify a set of queries. --- ...nalyzing-databases-with-the-codeql-cli.rst | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst b/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst index d3b04a32a0b..cac63f6d01f 100644 --- a/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst +++ b/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst @@ -135,6 +135,60 @@ pack names and use the ``--download`` flag:: The ``analyze`` command above runs the default suite from ``microsoft/coding-standards v1.0.0`` and the latest version of ``github/security-queries`` on the specified database. For further information about default suites, see ":ref:`Publishing and using CodeQL packs `". +Running a subset of queries in a CodeQL pack +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Additionally, you can include a path at the end of a pack specification to run a subset of queries inside the pack. This applies to any command that locates or runs queries within a pack. + +The complete way to specify a set of queries is in the form ``scope/name@range:path``, where: + +- ``scope/name`` is the qualified name of a CodeQL pack. +- ``range`` is a `semver range `_. +- ``path`` is a file system path to a single query, a directory containing queries, or a query suite file. + +If a ``scope/name`` is specified, the ``range`` and ``path`` are +optional. A missing ``range`` implies the latest version of the +specified pack. A missing ``path`` implies the default query suite +of the specified pack. + +The ``path`` can be one of a ``*.ql`` query file, a directory +containing one or more queries, or a ``.qls`` query suite file. If +there is no pack name specified, then a ``path`` must be provided, +and will be interpreted relative to the current working directory +of the current process. + +If a ``scope/name`` and ``path`` are specified, then the ``path`` cannot +be absolute. It is considered relative to the root of the CodeQL +pack. + +The relevant commands are: + +* `codeql database analyze <../manual/database-analyze>`__. +* `codeql database run-queries <../manual/database-run-queries>`__. +* `codeql execute queries <../manual/execute-queries>`__. +* `codeql resolve queries <../manual/resolve-queries>`__. + +For example:: + + # Analyze a database using all queries in the experimental/Security folder within the codeql/cpp-queries + # CodeQL query pack. + codeql database analyze --format=sarif-latest --output=results \ + codeql/cpp-queries:experimental/Security + + # Analyse using only the RedundantNullCheckParam.ql query in the codeql/cpp-queries CodeQL query pack. + codeql database analyze --format=sarif-latest --output=results \ + 'codeql/cpp-queries:experimental/Likely Bugs/RedundantNullCheckParam.ql' + + # Analyse using the cpp-security-and-quality.qls query suite in the codeql/cpp-queries CodeQL query pack. + codeql database analyze --format=sarif-latest --output=results \ + 'codeql/cpp-queries:codeql-suites/cpp-security-and-quality.qls' + + # Analyse using the cpp-security-and-quality.qls query suite from a version of the codeql/cpp-queries pack + # that is >= 0.0.3 and < 0.1.0 (the highest compatible version will be chosen). + # All valid semver ranges are allowed. See https://docs.npmjs.com/cli/v6/using-npm/semver#ranges + codeql database analyze --format=sarif-latest --output=results \ + 'codeql/cpp-queries@~0.0.3:codeql-suites/cpp-security-and-quality.qls' + For more information about CodeQL packs, see :doc:`About CodeQL Packs `. Running query suites From 5931ea4ab80113b66dfa5bf26df30cd80aefe102 Mon Sep 17 00:00:00 2001 From: Henry Mercer Date: Wed, 15 Jun 2022 16:42:32 -0700 Subject: [PATCH 033/505] Add section on managing packs on GHES --- .../publishing-and-using-codeql-packs.rst | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/docs/codeql/codeql-cli/publishing-and-using-codeql-packs.rst b/docs/codeql/codeql-cli/publishing-and-using-codeql-packs.rst index d28e27e10d7..0a1affb782b 100644 --- a/docs/codeql/codeql-cli/publishing-and-using-codeql-packs.rst +++ b/docs/codeql/codeql-cli/publishing-and-using-codeql-packs.rst @@ -72,3 +72,21 @@ The ``analyze`` command will run the default suite of any specified CodeQL packs :: codeql analyze / / + +Managing packs on GitHub Enterprise Server +------------------------------------------ + +By default, CodeQL will download packs from and publish packs to the GitHub.com Container registry. +You can manage packs on GitHub Enterprise Server 3.6 and later by creating a ``qlconfig.yml`` file to tell CodeQL which Container registry to use for each pack. +Create the ``~/.codeql/qlconfig.yml`` file using your preferred text editor, and add entries to specify which registry to use for each pack name pattern. +For example, the following ``qlconfig.yml`` file associates all packs with the Container registry for the GitHub Enterprise Server at ``GHE_HOSTNAME``, except packs matching ``codeql/*``, which are associated with the GitHub.com Container registry: + +.. code-block:: yaml + + registries: + - packages: '*' + url: https://containers.GHE_HOSTNAME/v2/ + - packages: 'codeql/*' + url: https://ghcr.io/v2/ + +You can now use ``codeql pack publish``, ``codeql pack download``, and ``codeql database analyze`` to manage packs on GitHub Enterprise Server. From e4462b7aacd5ea62ea3676c2f7d8603a47c7b3aa Mon Sep 17 00:00:00 2001 From: Henry Mercer Date: Thu, 16 Jun 2022 14:35:55 -0700 Subject: [PATCH 034/505] Add a section on authenticating to Container registries --- .../publishing-and-using-codeql-packs.rst | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/docs/codeql/codeql-cli/publishing-and-using-codeql-packs.rst b/docs/codeql/codeql-cli/publishing-and-using-codeql-packs.rst index 0a1affb782b..4e7b8d452ac 100644 --- a/docs/codeql/codeql-cli/publishing-and-using-codeql-packs.rst +++ b/docs/codeql/codeql-cli/publishing-and-using-codeql-packs.rst @@ -90,3 +90,21 @@ For example, the following ``qlconfig.yml`` file associates all packs with the C url: https://ghcr.io/v2/ You can now use ``codeql pack publish``, ``codeql pack download``, and ``codeql database analyze`` to manage packs on GitHub Enterprise Server. + +Authenticating to GitHub Container registries +--------------------------------------------- + +You can download a private pack or publish a pack by authenticating to the appropriate GitHub Container registry. + +You can authenticate to the GitHub.com Container registry in two ways: + +1. Pass the ``--github-auth-stdin`` option to the CodeQL CLI, then supply a GitHub Apps token or personal access token via standard input. +2. Set the ``GITHUB_TOKEN`` environment variable to a GitHub Apps token or personal access token. + +Similarly, you can authenticate to a GHES Container registry, or authenticate to multiple registries simultaneously (for example to download or analyze private packs from multiple registries) in two ways: + +1. Pass the ``--registries-auth-stdin`` option to the CodeQL CLI, then supply a registry authentication string via standard input. +2. Set the ``CODEQL_REGISTRIES_AUTH`` environment variable to a registry authentication string. + +A registry authentication string is a comma-separated list of ``=`` pairs, where ``registry-url`` is a GitHub Container registry URL, for example ``https://containers.GHE_HOSTNAME/v2/`` and ``token`` is a GitHub Apps token or personal access token for that GitHub Container registry. +This ensures that each token is only passed to the Container registry you specify. From 4733653939b7d895c0f624086e7c8ad5d1b36652 Mon Sep 17 00:00:00 2001 From: Henry Mercer Date: Thu, 16 Jun 2022 15:08:16 -0700 Subject: [PATCH 035/505] Add a note on how to install dependencies from GHES --- .../codeql-cli/creating-and-working-with-codeql-packs.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/codeql/codeql-cli/creating-and-working-with-codeql-packs.rst b/docs/codeql/codeql-cli/creating-and-working-with-codeql-packs.rst index 6373440bcbb..e6a679c89a9 100644 --- a/docs/codeql/codeql-cli/creating-and-working-with-codeql-packs.rst +++ b/docs/codeql/codeql-cli/creating-and-working-with-codeql-packs.rst @@ -68,3 +68,11 @@ This command downloads all dependencies to the shared cache on the local disk. Note Running the ``codeql pack add`` and ``codeql pack install`` commands will generate or update the ``qlpack.lock.yml`` file. This file should be checked-in to version control. The ``qlpack.lock.yml`` file contains the precise version numbers used by the pack. + +.. pull-quote:: + + Note + + By default ``codeql pack install`` will install dependencies from the GitHub.com Container registry. + You can install dependencies from a GitHub Enterprise Server Container registry by creating a ``qlconfig.yml`` file. + For more information, see ":doc:`Publishing and using CodeQL packs `." From f1b0a814e057d02a72e95a1cc99313359199fe3d Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Fri, 17 Jun 2022 15:04:57 +0200 Subject: [PATCH 036/505] Python: Apply suggestions from code review Co-authored-by: yoff --- python/ql/lib/semmle/python/ApiGraphs.qll | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/python/ql/lib/semmle/python/ApiGraphs.qll b/python/ql/lib/semmle/python/ApiGraphs.qll index 1023347b832..256fe6442f4 100644 --- a/python/ql/lib/semmle/python/ApiGraphs.qll +++ b/python/ql/lib/semmle/python/ApiGraphs.qll @@ -597,11 +597,9 @@ module API { // "benign" and let subclasses edges flow through anyway. // see example in https://github.com/django/django/blob/c2250cfb80e27cdf8d098428824da2800a18cadf/tests/auth_tests/test_views.py#L40-L46 ( - not exists(clsExpr.getADecorator()) and ref.asExpr() = clsExpr or - ref.asExpr() = clsExpr.getADecoratorCall() and - not exists(PY::Call otherDecorator | otherDecorator.getArg(0) = ref.asExpr()) + ref.asExpr() = clsExpr.getADecoratorCall() ) ) or From 8aa2602d9e24a0e2b3ebae1836630c818116f376 Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Sat, 18 Jun 2022 03:09:04 +0000 Subject: [PATCH 037/505] trying to hone in on eq comparison and include? --- .../ManuallyCheckHttpVerb.ql | 56 ++++++++++--------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql b/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql index ab71ba45d46..ac34ad34088 100644 --- a/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql +++ b/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql @@ -1,6 +1,6 @@ /** * @name Manually checking http verb instead of using built in rails routes and protections - * @description Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. + * @description Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. * @kind problem * @problem.severity error * @security-severity 7.0 @@ -20,59 +20,63 @@ class ManuallyCheckHttpVerb extends DataFlow::CallNode { this instanceof CheckPostRequest or this instanceof CheckDeleteRequest or this instanceof CheckHeadRequest or - this.asExpr().getExpr() instanceof CheckRequestMethodFromEnv + this instanceof CheckRequestMethodFromEnv } } -class CheckRequestMethodFromEnv extends ComparisonOperation { +class CheckRequestMethodFromEnv extends DataFlow::CallNode { CheckRequestMethodFromEnv() { - this.getAnOperand() instanceof GetRequestMethodFromEnv + // is this node an instance of `env["REQUEST_METHOD"] + this.getExprNode().getNode() instanceof GetRequestMethodFromEnv and + ( + // and is this node a param of a call to `.include?` + exists(MethodCall call | call.getAnArgument() = this.getExprNode().getNode() | + call.getMethodName() = "include?" + ) + or + exists(DataFlow::Node node | + node.asExpr().getExpr().(MethodCall).getMethodName() = "include?" + | + node.getALocalSource() = this + ) + or + // or is this node on either size of an equality comparison + exists(EqualityOperation eq | eq.getAChild() = this.getExprNode().getNode()) + ) } } class GetRequestMethodFromEnv extends ElementReference { GetRequestMethodFromEnv() { - this.getAChild+().toString() = "REQUEST_METHOD" // and - // this.getReceiver().toString() = "env" + this.getAChild+().toString() = "REQUEST_METHOD" and + this.getAChild+().toString() = "env" } } class CheckGetRequest extends DataFlow::CallNode { - CheckGetRequest() { - this.getMethodName() = "get?" - } + CheckGetRequest() { this.getMethodName() = "get?" } } class CheckPostRequest extends DataFlow::CallNode { - CheckPostRequest() { - this.getMethodName() = "post?" - } + CheckPostRequest() { this.getMethodName() = "post?" } } class CheckPutRequest extends DataFlow::CallNode { - CheckPutRequest() { - this.getMethodName() = "put?" - } + CheckPutRequest() { this.getMethodName() = "put?" } } class CheckPatchRequest extends DataFlow::CallNode { - CheckPatchRequest() { - this.getMethodName() = "patch?" - } + CheckPatchRequest() { this.getMethodName() = "patch?" } } class CheckDeleteRequest extends DataFlow::CallNode { - CheckDeleteRequest() { - this.getMethodName() = "delete?" - } + CheckDeleteRequest() { this.getMethodName() = "delete?" } } class CheckHeadRequest extends DataFlow::CallNode { - CheckHeadRequest() { - this.getMethodName() = "head?" - } + CheckHeadRequest() { this.getMethodName() = "head?" } } from ManuallyCheckHttpVerb check -where check.asExpr().getExpr().getAControlFlowNode().isCondition() -select check, "Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods." \ No newline at end of file +select check, + "Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods." From 059c4d38ad97b4972f0be1e4e878fbd32ad3b10c Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Sat, 18 Jun 2022 18:26:45 +0000 Subject: [PATCH 038/505] refine query to use appropriate types --- .../ManuallyCheckHttpVerb.ql | 26 ++++++++++--------- .../ManuallyCheckHttpVerb.expected | 0 2 files changed, 14 insertions(+), 12 deletions(-) create mode 100644 ruby/ql/test/query-tests/security/manually-check-http-verb/ManuallyCheckHttpVerb.expected diff --git a/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql b/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql index ac34ad34088..7396e75b920 100644 --- a/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql +++ b/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql @@ -12,15 +12,14 @@ import ruby import codeql.ruby.DataFlow -class ManuallyCheckHttpVerb extends DataFlow::CallNode { - ManuallyCheckHttpVerb() { +class HttpVerbMethod extends MethodCall { + HttpVerbMethod() { this instanceof CheckGetRequest or this instanceof CheckPostRequest or this instanceof CheckPatchRequest or this instanceof CheckPostRequest or this instanceof CheckDeleteRequest or - this instanceof CheckHeadRequest or - this instanceof CheckRequestMethodFromEnv + this instanceof CheckHeadRequest } } @@ -53,30 +52,33 @@ class GetRequestMethodFromEnv extends ElementReference { } } -class CheckGetRequest extends DataFlow::CallNode { +class CheckGetRequest extends MethodCall { CheckGetRequest() { this.getMethodName() = "get?" } } -class CheckPostRequest extends DataFlow::CallNode { +class CheckPostRequest extends MethodCall { CheckPostRequest() { this.getMethodName() = "post?" } } -class CheckPutRequest extends DataFlow::CallNode { +class CheckPutRequest extends MethodCall { CheckPutRequest() { this.getMethodName() = "put?" } } -class CheckPatchRequest extends DataFlow::CallNode { +class CheckPatchRequest extends MethodCall { CheckPatchRequest() { this.getMethodName() = "patch?" } } -class CheckDeleteRequest extends DataFlow::CallNode { +class CheckDeleteRequest extends MethodCall { CheckDeleteRequest() { this.getMethodName() = "delete?" } } -class CheckHeadRequest extends DataFlow::CallNode { +class CheckHeadRequest extends MethodCall { CheckHeadRequest() { this.getMethodName() = "head?" } } -from ManuallyCheckHttpVerb check -select check, +from CheckRequestMethodFromEnv env, AstNode node +where + node instanceof HttpVerbMethod or + node = env.asExpr().getExpr() +select node, "Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods." diff --git a/ruby/ql/test/query-tests/security/manually-check-http-verb/ManuallyCheckHttpVerb.expected b/ruby/ql/test/query-tests/security/manually-check-http-verb/ManuallyCheckHttpVerb.expected new file mode 100644 index 00000000000..e69de29bb2d From 8b3619102395283814944424ed142ee843a22a97 Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Sat, 18 Jun 2022 18:38:58 +0000 Subject: [PATCH 039/505] drop precision to low for now --- .../manually-check-http-verb/ManuallyCheckHttpVerb.ql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql b/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql index 7396e75b920..5be00b48464 100644 --- a/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql +++ b/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql @@ -3,8 +3,8 @@ * @description Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. * @kind problem * @problem.severity error - * @security-severity 7.0 - * @precision medium + * @security-severity 5.0 + * @precision low * @id rb/manually-checking-http-verb * @tags security */ From ecb2114b7bb709a67160334db0234172caed31e3 Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Sat, 18 Jun 2022 19:21:17 +0000 Subject: [PATCH 040/505] replace duplicate post with put --- .../manually-check-http-verb/ManuallyCheckHttpVerb.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql b/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql index 5be00b48464..6946d26a7e8 100644 --- a/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql +++ b/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql @@ -17,7 +17,7 @@ class HttpVerbMethod extends MethodCall { this instanceof CheckGetRequest or this instanceof CheckPostRequest or this instanceof CheckPatchRequest or - this instanceof CheckPostRequest or + this instanceof CheckPutRequest or this instanceof CheckDeleteRequest or this instanceof CheckHeadRequest } From 3478e7e9109fc216e741c42a999f56697c70bf1d Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Sat, 18 Jun 2022 20:43:58 +0000 Subject: [PATCH 041/505] first draft of weak params query --- .../experimental/weak-params/WeakParams.ql | 46 +++++++++++++++++++ .../security/weak-params/WeakParams.expected | 0 .../security/weak-params/WeakParams.qlref | 1 + .../security/weak-params/WeakParams.rb | 15 ++++++ 4 files changed, 62 insertions(+) create mode 100644 ruby/ql/src/experimental/weak-params/WeakParams.ql create mode 100644 ruby/ql/test/query-tests/security/weak-params/WeakParams.expected create mode 100644 ruby/ql/test/query-tests/security/weak-params/WeakParams.qlref create mode 100644 ruby/ql/test/query-tests/security/weak-params/WeakParams.rb diff --git a/ruby/ql/src/experimental/weak-params/WeakParams.ql b/ruby/ql/src/experimental/weak-params/WeakParams.ql new file mode 100644 index 00000000000..b756d6ed8b8 --- /dev/null +++ b/ruby/ql/src/experimental/weak-params/WeakParams.ql @@ -0,0 +1,46 @@ +/** + * @name Weak or direct parameter references are used + * @description Directly checking request parameters without following a strong params pattern can lead to unintentional avenues for injection attacks. + * @kind problem + * @problem.severity error + * @security-severity 5.0 + * @precision low + * @id rb/weak-params + * @tags security + */ + +import ruby + +class WeakParams extends AstNode { + WeakParams() { + this instanceof UnspecificParamsMethod or + this instanceof ParamsReference + } +} + +class StrongParamsMethod extends Method { + StrongParamsMethod() { this.getName().regexpMatch(".*_params") } +} + +class UnspecificParamsMethod extends MethodCall { + UnspecificParamsMethod() { + ( + this.getMethodName() = "expose_all" or + this.getMethodName() = "original_hash" or + this.getMethodName() = "path_parametes" or + this.getMethodName() = "query_parameters" or + this.getMethodName() = "request_parameters" or + this.getMethodName() = "GET" or + this.getMethodName() = "POST" + ) + } +} + +class ParamsReference extends ElementReference { + ParamsReference() { this.getAChild().toString() = "params" } +} + +from WeakParams params +where not params.getEnclosingMethod() instanceof StrongParamsMethod +select params, + "By exposing all keys in request parameters or by blindy accessing them, unintended parameters could be used and lead to mass-assignment or have other unexpected side-effects." diff --git a/ruby/ql/test/query-tests/security/weak-params/WeakParams.expected b/ruby/ql/test/query-tests/security/weak-params/WeakParams.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/ruby/ql/test/query-tests/security/weak-params/WeakParams.qlref b/ruby/ql/test/query-tests/security/weak-params/WeakParams.qlref new file mode 100644 index 00000000000..5350e4bf40a --- /dev/null +++ b/ruby/ql/test/query-tests/security/weak-params/WeakParams.qlref @@ -0,0 +1 @@ +experimental/weak-params/WeakParams.ql \ No newline at end of file diff --git a/ruby/ql/test/query-tests/security/weak-params/WeakParams.rb b/ruby/ql/test/query-tests/security/weak-params/WeakParams.rb new file mode 100644 index 00000000000..cc0dc80341f --- /dev/null +++ b/ruby/ql/test/query-tests/security/weak-params/WeakParams.rb @@ -0,0 +1,15 @@ +class TestController < ActionController::Base + def create + TestObject.new(request.request_parameters) + end + + def create_query + TestObject.new(request.query_parameters) + end + + # + def object_params + p = params.query_parameters + params.require(:uuid).permit(:notes) + end +end \ No newline at end of file From 3a4f0299c728b9fc81f65405dd3ee68d606c9546 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Fri, 17 Jun 2022 15:15:25 +0200 Subject: [PATCH 042/505] fix typo --- javascript/ql/src/Expressions/TypoDatabase.qll | 2 ++ ql/ql/src/codeql_ql/ast/internal/Predicate.qll | 2 +- ql/ql/src/codeql_ql/style/TypoDatabase.qll | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/javascript/ql/src/Expressions/TypoDatabase.qll b/javascript/ql/src/Expressions/TypoDatabase.qll index aad43d9d0cc..fadc1b8af75 100644 --- a/javascript/ql/src/Expressions/TypoDatabase.qll +++ b/javascript/ql/src/Expressions/TypoDatabase.qll @@ -5793,6 +5793,8 @@ predicate typos(string wrong, string right) { or wrong = "paramters" and right = "parameters" or + wrong = "parametarized" and right = "parameterized" + or wrong = "paranthesis" and right = "parenthesis" or wrong = "paraphenalia" and right = "paraphernalia" diff --git a/ql/ql/src/codeql_ql/ast/internal/Predicate.qll b/ql/ql/src/codeql_ql/ast/internal/Predicate.qll index eb7e298b7cd..0e614c23c25 100644 --- a/ql/ql/src/codeql_ql/ast/internal/Predicate.qll +++ b/ql/ql/src/codeql_ql/ast/internal/Predicate.qll @@ -176,7 +176,7 @@ module PredConsistency { c > 1 and resolvePredicateExpr(pe, p) } - // This can happen with parametarized modules + // This can happen with parameterized modules /* * query predicate multipleResolveCall(Call call, int c, PredicateOrBuiltin p) { * c = diff --git a/ql/ql/src/codeql_ql/style/TypoDatabase.qll b/ql/ql/src/codeql_ql/style/TypoDatabase.qll index aad43d9d0cc..fadc1b8af75 100644 --- a/ql/ql/src/codeql_ql/style/TypoDatabase.qll +++ b/ql/ql/src/codeql_ql/style/TypoDatabase.qll @@ -5793,6 +5793,8 @@ predicate typos(string wrong, string right) { or wrong = "paramters" and right = "parameters" or + wrong = "parametarized" and right = "parameterized" + or wrong = "paranthesis" and right = "parenthesis" or wrong = "paraphenalia" and right = "paraphernalia" From a59f0d36f55fac849e2fa97d7db40dbddd929c2e Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Fri, 17 Jun 2022 18:06:12 +0200 Subject: [PATCH 043/505] run the implicit-this patch on QL-for-QL --- ql/ql/src/codeql_ql/ast/Ast.qll | 2 +- ql/ql/src/codeql_ql/dataflow/DataFlow.qll | 16 +++++++++------- ql/ql/src/queries/reports/FrameworkCoverage.ql | 6 ++++-- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/ql/ql/src/codeql_ql/ast/Ast.qll b/ql/ql/src/codeql_ql/ast/Ast.qll index fa551b0de83..ffd8d07cd2a 100644 --- a/ql/ql/src/codeql_ql/ast/Ast.qll +++ b/ql/ql/src/codeql_ql/ast/Ast.qll @@ -1167,7 +1167,7 @@ class Import extends TImport, ModuleMember, TypeRef { */ string getImportString() { exists(string selec | - not exists(getSelectionName(_)) and selec = "" + not exists(this.getSelectionName(_)) and selec = "" or selec = "::" + strictconcat(int i, string q | q = this.getSelectionName(i) | q, "::" order by i) diff --git a/ql/ql/src/codeql_ql/dataflow/DataFlow.qll b/ql/ql/src/codeql_ql/dataflow/DataFlow.qll index da8bc1da837..c9043416bae 100644 --- a/ql/ql/src/codeql_ql/dataflow/DataFlow.qll +++ b/ql/ql/src/codeql_ql/dataflow/DataFlow.qll @@ -204,7 +204,7 @@ class SuperNode extends LocalFlow::TSuperNode { Node getANode() { LocalFlow::getRepr(result) = repr } /** Gets an AST node from any of the nodes in this super node. */ - AstNode asAstNode() { result = getANode().asAstNode() } + AstNode asAstNode() { result = this.getANode().asAstNode() } /** * Gets a single node from this super node. @@ -214,23 +214,25 @@ class SuperNode extends LocalFlow::TSuperNode { * - An `AstNodeNode` is preferred over other nodes. * - A node occurring earlier is preferred over one occurring later. */ - Node getArbitraryRepr() { result = min(Node n | n = getANode() | n order by getInternalId(n)) } + Node getArbitraryRepr() { + result = min(Node n | n = this.getANode() | n order by getInternalId(n)) + } /** * Gets the predicate containing all nodes that are part of this super node. */ - Predicate getEnclosingPredicate() { result = getANode().getEnclosingPredicate() } + Predicate getEnclosingPredicate() { result = this.getANode().getEnclosingPredicate() } /** Gets a string representation of this super node. */ string toString() { exists(int c | - c = strictcount(getANode()) and - result = "Super node of " + c + " nodes in " + getEnclosingPredicate().getName() + c = strictcount(this.getANode()) and + result = "Super node of " + c + " nodes in " + this.getEnclosingPredicate().getName() ) } /** Gets the location of an arbitrary node in this super node. */ - Location getLocation() { result = getArbitraryRepr().getLocation() } + Location getLocation() { result = this.getArbitraryRepr().getLocation() } /** Gets any member call whose receiver is in the same super node. */ MemberCall getALocalMemberCall() { superNode(result.getBase()) = this } @@ -287,7 +289,7 @@ class SuperNode extends LocalFlow::TSuperNode { cached private string getAStringValue(Tracker t) { t.start() and - result = asAstNode().(String).getValue() + result = this.asAstNode().(String).getValue() or exists(SuperNode pred, Tracker t2 | this = pred.track(t2, t) and diff --git a/ql/ql/src/queries/reports/FrameworkCoverage.ql b/ql/ql/src/queries/reports/FrameworkCoverage.ql index 7f5eb7f5310..f394a5a0091 100644 --- a/ql/ql/src/queries/reports/FrameworkCoverage.ql +++ b/ql/ql/src/queries/reports/FrameworkCoverage.ql @@ -18,11 +18,13 @@ class PackageImportCall extends PredicateCall { PackageImportCall() { this.getQualifier().getName() = ["API", "DataFlow"] and this.getPredicateName() = ["moduleImport", "moduleMember"] and - not isExcludedFile(getLocation().getFile()) + not isExcludedFile(this.getLocation().getFile()) } /** Gets the name of a package referenced by this call */ - string getAPackageName() { result = DataFlow::superNode(getArgument(0)).getAStringValueNoCall() } + string getAPackageName() { + result = DataFlow::superNode(this.getArgument(0)).getAStringValueNoCall() + } } /** Gets a reference to `package` or any transitive member thereof. */ From 7e93416e9728ba24d2936027d033607c952621e1 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Fri, 17 Jun 2022 21:32:19 +0200 Subject: [PATCH 044/505] only resolve module types if we know that the TypeExpr could possibly resolve to a module --- ql/ql/src/codeql_ql/ast/Ast.qll | 13 +++++++++++-- ql/ql/test/queries/style/RedundantCast/Foo.qll | 11 +++++++++++ .../style/RedundantCast/RedundantCast.expected | 3 +++ .../queries/style/RedundantCast/RedundantCast.qlref | 1 + 4 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 ql/ql/test/queries/style/RedundantCast/Foo.qll create mode 100644 ql/ql/test/queries/style/RedundantCast/RedundantCast.expected create mode 100644 ql/ql/test/queries/style/RedundantCast/RedundantCast.qlref diff --git a/ql/ql/src/codeql_ql/ast/Ast.qll b/ql/ql/src/codeql_ql/ast/Ast.qll index ffd8d07cd2a..09f6e35171f 100644 --- a/ql/ql/src/codeql_ql/ast/Ast.qll +++ b/ql/ql/src/codeql_ql/ast/Ast.qll @@ -682,8 +682,17 @@ class TypeExpr extends TType, TypeRef { // resolve type resolveTypeExpr(this, result) or - // if it resolves to a module - exists(FileOrModule mod | resolveModuleRef(this, mod) | result = mod.toType()) + // if it resolves to a module, + exists(FileOrModule mod | resolveModuleRef(this, mod) | result = mod.toType()) and + result instanceof ModuleType and + // we can get spurious results in some cases, so we restrict to where it is possible to have a module. + ( + // only possible if this is inside a moduleInstantiation. + this = any(ModuleExpr mod).getArgument(_).asType() + or + // or if it's a parameter to a parameterized module + this = any(SignatureExpr sig, Module mod | mod.hasParameter(_, _, sig) | sig).asType() + ) } override AstNode getAChild(string pred) { diff --git a/ql/ql/test/queries/style/RedundantCast/Foo.qll b/ql/ql/test/queries/style/RedundantCast/Foo.qll new file mode 100644 index 00000000000..d993f654bc4 --- /dev/null +++ b/ql/ql/test/queries/style/RedundantCast/Foo.qll @@ -0,0 +1,11 @@ +class Foo extends string { + Foo() { this = "Foo" } +} + +predicate test(Foo f) { f.(Foo).toString() = "X" } + +predicate test2(Foo a, Foo b) { a.(Foo) = b } + +predicate called(Foo a) { a.toString() = "X" } + +predicate test3(string s) { called(s.(Foo)) } diff --git a/ql/ql/test/queries/style/RedundantCast/RedundantCast.expected b/ql/ql/test/queries/style/RedundantCast/RedundantCast.expected new file mode 100644 index 00000000000..e4e57083633 --- /dev/null +++ b/ql/ql/test/queries/style/RedundantCast/RedundantCast.expected @@ -0,0 +1,3 @@ +| Foo.qll:5:25:5:31 | InlineCast | Redundant cast to $@ | Foo.qll:5:28:5:30 | TypeExpr | Foo | +| Foo.qll:7:33:7:39 | InlineCast | Redundant cast to $@ | Foo.qll:7:36:7:38 | TypeExpr | Foo | +| Foo.qll:11:36:11:42 | InlineCast | Redundant cast to $@ | Foo.qll:11:39:11:41 | TypeExpr | Foo | diff --git a/ql/ql/test/queries/style/RedundantCast/RedundantCast.qlref b/ql/ql/test/queries/style/RedundantCast/RedundantCast.qlref new file mode 100644 index 00000000000..659062d3ae5 --- /dev/null +++ b/ql/ql/test/queries/style/RedundantCast/RedundantCast.qlref @@ -0,0 +1 @@ +queries/style/RedundantCast.ql From 0391db67876892ef943108e44d52725f9dbbcfb5 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Fri, 17 Jun 2022 22:52:17 +0200 Subject: [PATCH 045/505] simplify some code based on review --- ql/ql/src/codeql_ql/ast/Ast.qll | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/ql/ql/src/codeql_ql/ast/Ast.qll b/ql/ql/src/codeql_ql/ast/Ast.qll index 09f6e35171f..a237e1f6b58 100644 --- a/ql/ql/src/codeql_ql/ast/Ast.qll +++ b/ql/ql/src/codeql_ql/ast/Ast.qll @@ -2226,9 +2226,7 @@ class ModuleExpr extends TModuleExpr, TypeRef { or not exists(me.getName()) and result = me.getChild().(QL::SimpleId).getValue() or - exists(QL::ModuleInstantiation instantiation | instantiation.getParent() = me | - result = instantiation.getName().getChild().getValue() - ) + result = me.getChild().(QL::ModuleInstantiation).getName().getChild().getValue() } /** @@ -2263,9 +2261,7 @@ class ModuleExpr extends TModuleExpr, TypeRef { * The result is either a `PredicateExpr` or a `TypeExpr`. */ SignatureExpr getArgument(int i) { - exists(QL::ModuleInstantiation instantiation | instantiation.getParent() = me | - result.toQL() = instantiation.getChild(i) - ) + result.toQL() = me.getChild().(QL::ModuleInstantiation).getChild(i) } } From 638a886dfef9d4601feca560df5225071a22e1a8 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Sat, 18 Jun 2022 16:43:56 +0200 Subject: [PATCH 046/505] move create-extractor-pack to a `scripts` folder --- .github/workflows/ql-for-ql-dataset_measure.yml | 2 +- .github/workflows/ql-for-ql-tests.yml | 2 +- ql/README.md | 4 ++-- ql/{ => scripts}/create-extractor-pack.ps1 | 0 ql/{ => scripts}/create-extractor-pack.sh | 0 5 files changed, 4 insertions(+), 4 deletions(-) rename ql/{ => scripts}/create-extractor-pack.ps1 (100%) rename ql/{ => scripts}/create-extractor-pack.sh (100%) diff --git a/.github/workflows/ql-for-ql-dataset_measure.yml b/.github/workflows/ql-for-ql-dataset_measure.yml index cf3b696f3b8..a5ed2e9b266 100644 --- a/.github/workflows/ql-for-ql-dataset_measure.yml +++ b/.github/workflows/ql-for-ql-dataset_measure.yml @@ -36,7 +36,7 @@ jobs: ql/target key: ${{ runner.os }}-qltest-cargo-${{ hashFiles('ql/**/Cargo.lock') }} - name: Build Extractor - run: cd ql; env "PATH=$PATH:`dirname ${CODEQL}`" ./create-extractor-pack.sh + run: cd ql; env "PATH=$PATH:`dirname ${CODEQL}`" ./scripts/create-extractor-pack.sh env: CODEQL: ${{ steps.find-codeql.outputs.codeql-path }} - name: Checkout ${{ matrix.repo }} diff --git a/.github/workflows/ql-for-ql-tests.yml b/.github/workflows/ql-for-ql-tests.yml index 3b0a4963b79..b016f21f2b9 100644 --- a/.github/workflows/ql-for-ql-tests.yml +++ b/.github/workflows/ql-for-ql-tests.yml @@ -36,7 +36,7 @@ jobs: run: | cd ql; codeqlpath=$(dirname ${{ steps.find-codeql.outputs.codeql-path }}); - env "PATH=$PATH:$codeqlpath" ./create-extractor-pack.sh + env "PATH=$PATH:$codeqlpath" ./scripts/create-extractor-pack.sh - name: Run QL tests run: | "${CODEQL}" test run --check-databases --check-unused-labels --check-repeated-labels --check-redefined-labels --check-use-before-definition --search-path "${{ github.workspace }}/ql/extractor-pack" --consistency-queries ql/ql/consistency-queries ql/ql/test diff --git a/ql/README.md b/ql/README.md index 4bb5e30ba84..29b5aaef63c 100644 --- a/ql/README.md +++ b/ql/README.md @@ -21,14 +21,14 @@ cargo build --release The generated `ql/src/ql.dbscheme` and `ql/src/codeql_ql/ast/internal/TreeSitter.qll` files are included in the repository, but they can be re-generated as follows: ```bash -./create-extractor-pack.sh +./scripts/create-extractor-pack.sh ``` ## Building a CodeQL database for a QL program First, get an extractor pack: -Run `./create-extractor-pack.sh` (Linux/Mac) or `.\create-extractor-pack.ps1` (Windows PowerShell) and the pack will be created in the `extractor-pack` directory. +Run `./scripts/create-extractor-pack.sh` (Linux/Mac) or `.\scripts\create-extractor-pack.ps1` (Windows PowerShell) and the pack will be created in the `extractor-pack` directory. Then run diff --git a/ql/create-extractor-pack.ps1 b/ql/scripts/create-extractor-pack.ps1 similarity index 100% rename from ql/create-extractor-pack.ps1 rename to ql/scripts/create-extractor-pack.ps1 diff --git a/ql/create-extractor-pack.sh b/ql/scripts/create-extractor-pack.sh similarity index 100% rename from ql/create-extractor-pack.sh rename to ql/scripts/create-extractor-pack.sh From 6e2f3e2fcb8097e48195491326f96e975587504e Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Sat, 18 Jun 2022 16:44:10 +0200 Subject: [PATCH 047/505] merge all .sarif files at the end of the QL-for-QL workflow --- .github/workflows/ql-for-ql-build.yml | 27 ++++++++++++++++++++++++--- ql/scripts/merge-sarif.js | 26 ++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 ql/scripts/merge-sarif.js diff --git a/.github/workflows/ql-for-ql-build.yml b/.github/workflows/ql-for-ql-build.yml index 6b4f6a0abee..ffb4ad1cbc0 100644 --- a/.github/workflows/ql-for-ql-build.yml +++ b/.github/workflows/ql-for-ql-build.yml @@ -50,9 +50,6 @@ jobs: path: ${{ runner.temp }}/query-pack.zip extractors: - strategy: - fail-fast: false - runs-on: ubuntu-latest steps: @@ -201,3 +198,27 @@ jobs: name: ${{ matrix.folder }}.sarif path: ${{ matrix.folder }}.sarif + combine: + runs-on: ubuntu-latest + needs: + - analyze + + steps: + - uses: actions/checkout@v3 + - name: Make a folder for artifacts. + run: mkdir -p results + - name: Download all sarif files + uses: actions/download-artifact@v3 + with: + path: results + - uses: actions/setup-node@v3 + with: + node-version: 16 + - name: Combine all sarif files + run: | + node ./ql/scripts/merge-sarif.js results/**/*.sarif combined.sarif + - name: Upload combined sarif file + uses: actions/upload-artifact@v3 + with: + name: combined.sarif + path: combined.sarif diff --git a/ql/scripts/merge-sarif.js b/ql/scripts/merge-sarif.js new file mode 100644 index 00000000000..c7507986d5a --- /dev/null +++ b/ql/scripts/merge-sarif.js @@ -0,0 +1,26 @@ +var fs = require("fs"); + +// first a list of files to merge, and the last argument is the output file. +async function main(files) { + const inputs = files + .slice(0, -1) + .map((file) => fs.readFileSync(file)) + .map((data) => JSON.parse(data)); + const out = inputs[0]; // just arbitrarily take the first one + const outFile = files[files.length - 1]; + + const combinedResults = []; + + for (const sarif of inputs) { + combinedResults.push(...sarif.runs[0].results); + } + + out.runs[0].artifacts = []; // the indexes in these won't make sense, so I hope this works. + out.runs[0].results = combinedResults; + + // workaround until https://github.com/microsoft/sarif-vscode-extension/pull/436/ is part of a release + out["$schema"] = "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0"; + + fs.writeFileSync(outFile, JSON.stringify(out, null, 2)); +} +main(process.argv.splice(2)); From 1856e2b3891c6b2e534057bdd763332208729aa3 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Sun, 19 Jun 2022 13:12:38 +0200 Subject: [PATCH 048/505] fixup the $schema in all .sarif files --- .github/workflows/ql-for-ql-build.yml | 3 +++ ql/scripts/merge-sarif.js | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ql-for-ql-build.yml b/.github/workflows/ql-for-ql-build.yml index ffb4ad1cbc0..95b93dd772c 100644 --- a/.github/workflows/ql-for-ql-build.yml +++ b/.github/workflows/ql-for-ql-build.yml @@ -192,6 +192,9 @@ jobs: category: "ql-for-ql-${{ matrix.folder }}" - name: Copy sarif file to CWD run: cp ../results/ql.sarif ./${{ matrix.folder }}.sarif + - name: Fixup the $scema in sarif # Until https://github.com/microsoft/sarif-vscode-extension/pull/436/ is part in a stable release + run: | + sed -i 's/\$schema.*/\$schema": "https:\/\/raw.githubusercontent.com\/oasis-tcs\/sarif-spec\/master\/Schemata\/sarif-schema-2.1.0",/' ${{ matrix.folder }}.sarif - name: Sarif as artifact uses: actions/upload-artifact@v3 with: diff --git a/ql/scripts/merge-sarif.js b/ql/scripts/merge-sarif.js index c7507986d5a..5c781f1b67d 100644 --- a/ql/scripts/merge-sarif.js +++ b/ql/scripts/merge-sarif.js @@ -18,9 +18,6 @@ async function main(files) { out.runs[0].artifacts = []; // the indexes in these won't make sense, so I hope this works. out.runs[0].results = combinedResults; - // workaround until https://github.com/microsoft/sarif-vscode-extension/pull/436/ is part of a release - out["$schema"] = "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0"; - fs.writeFileSync(outFile, JSON.stringify(out, null, 2)); } main(process.argv.splice(2)); From 26df367a8a9b6be00fcabed22e9f0c352e167cfe Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Sun, 19 Jun 2022 18:59:00 +0200 Subject: [PATCH 049/505] fix some instances of spuriously resolving to multiple predicates --- .../src/codeql_ql/ast/internal/Predicate.qll | 7 +++-- ql/ql/test/callgraph/MultiResolve.qll | 26 +++++++++++++++++++ ql/ql/test/callgraph/callgraph.expected | 5 ++++ 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 ql/ql/test/callgraph/MultiResolve.qll diff --git a/ql/ql/src/codeql_ql/ast/internal/Predicate.qll b/ql/ql/src/codeql_ql/ast/internal/Predicate.qll index 0e614c23c25..b4a5eaa6574 100644 --- a/ql/ql/src/codeql_ql/ast/internal/Predicate.qll +++ b/ql/ql/src/codeql_ql/ast/internal/Predicate.qll @@ -31,7 +31,8 @@ private predicate definesPredicate( name = alias.getName() and resolvePredicateExpr(alias.getAlias(), p) and public = getPublicBool(alias) and - arity = alias.getArity() + arity = alias.getArity() and + arity = p.getArity() ) ) } @@ -53,10 +54,12 @@ private module Cached { } private predicate resolvePredicateCall(PredicateCall pc, PredicateOrBuiltin p) { + // calls to class methods exists(Class c, ClassType t | c = pc.getParent*() and t = c.getType() and - p = t.getClassPredicate(pc.getPredicateName(), pc.getNumberOfArguments()) + p = t.getClassPredicate(pc.getPredicateName(), pc.getNumberOfArguments()) and + not exists(pc.getQualifier()) // no module qualifier, because then it's not a call to a class method. ) or exists(FileOrModule m, boolean public | diff --git a/ql/ql/test/callgraph/MultiResolve.qll b/ql/ql/test/callgraph/MultiResolve.qll new file mode 100644 index 00000000000..161d86077ad --- /dev/null +++ b/ql/ql/test/callgraph/MultiResolve.qll @@ -0,0 +1,26 @@ +predicate foo(int a, int b) { + a = 2 and + b = 2 +} + +predicate foo(int a, int b, int c) { + a = 2 and + b = 2 and + c = 2 +} + +predicate myFoo = foo/2; + +predicate test(int i) { myFoo(i, i) } // <- should only resolve to the `foo` with 2 arguments (and the `myFoo` alias). + +module MyMod { + predicate bar() { any() } + + class Bar extends int { + Bar() { this = 42 } + + predicate bar() { + MyMod::bar() // <- should resolve to the module's predicate. + } + } +} diff --git a/ql/ql/test/callgraph/callgraph.expected b/ql/ql/test/callgraph/callgraph.expected index 97c84034602..b640afaecc6 100644 --- a/ql/ql/test/callgraph/callgraph.expected +++ b/ql/ql/test/callgraph/callgraph.expected @@ -14,6 +14,9 @@ getTarget | Foo.qll:31:5:31:12 | PredicateCall | Foo.qll:24:3:24:32 | ClasslessPredicate alias0 | | Foo.qll:36:36:36:65 | MemberCall | file://:0:0:0:0 | replaceAll | | Foo.qll:38:39:38:67 | MemberCall | file://:0:0:0:0 | regexpCapture | +| MultiResolve.qll:14:25:14:35 | PredicateCall | MultiResolve.qll:1:1:4:1 | ClasslessPredicate foo | +| MultiResolve.qll:14:25:14:35 | PredicateCall | MultiResolve.qll:12:1:12:24 | ClasslessPredicate myFoo | +| MultiResolve.qll:25:7:25:18 | PredicateCall | MultiResolve.qll:17:3:19:3 | ClasslessPredicate bar | | Overrides.qll:8:30:8:39 | MemberCall | Overrides.qll:6:3:6:29 | ClassPredicate bar | | Overrides.qll:16:39:16:48 | MemberCall | Overrides.qll:6:3:6:29 | ClassPredicate bar | | Overrides.qll:16:39:16:48 | MemberCall | Overrides.qll:14:12:14:43 | ClassPredicate bar | @@ -43,5 +46,7 @@ exprPredicate | Foo.qll:26:22:26:31 | predicate | Foo.qll:20:3:20:54 | ClasslessPredicate myThing2 | | Foo.qll:47:55:47:62 | predicate | Foo.qll:42:20:42:27 | NewTypeBranch MkRoot | | Foo.qll:47:65:47:70 | predicate | Foo.qll:44:9:44:56 | ClasslessPredicate edge | +| MultiResolve.qll:12:19:12:23 | predicate | MultiResolve.qll:1:1:4:1 | ClasslessPredicate foo | +| MultiResolve.qll:12:19:12:23 | predicate | MultiResolve.qll:6:1:10:1 | ClasslessPredicate foo | | ParamModules.qll:4:18:4:25 | predicate | ParamModules.qll:2:13:2:36 | ClasslessPredicate fooSig | | ParamModules.qll:10:34:10:40 | predicate | ParamModules.qll:8:3:8:35 | ClasslessPredicate myFoo | From 115110475d3ba345a21b38c8b8891cba5835730d Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Sun, 19 Jun 2022 20:09:01 +0200 Subject: [PATCH 050/505] fix `getName()` on module instantiations --- ql/ql/src/codeql_ql/ast/Ast.qll | 4 ++-- ql/ql/src/codeql_ql/ast/internal/Module.qll | 16 ++++++++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/ql/ql/src/codeql_ql/ast/Ast.qll b/ql/ql/src/codeql_ql/ast/Ast.qll index a237e1f6b58..3a0063aaa76 100644 --- a/ql/ql/src/codeql_ql/ast/Ast.qll +++ b/ql/ql/src/codeql_ql/ast/Ast.qll @@ -2226,7 +2226,7 @@ class ModuleExpr extends TModuleExpr, TypeRef { or not exists(me.getName()) and result = me.getChild().(QL::SimpleId).getValue() or - result = me.getChild().(QL::ModuleInstantiation).getName().getChild().getValue() + result = me.getAFieldOrChild().(QL::ModuleInstantiation).getName().getChild().getValue() } /** @@ -2261,7 +2261,7 @@ class ModuleExpr extends TModuleExpr, TypeRef { * The result is either a `PredicateExpr` or a `TypeExpr`. */ SignatureExpr getArgument(int i) { - result.toQL() = me.getChild().(QL::ModuleInstantiation).getChild(i) + result.toQL() = me.getAFieldOrChild().(QL::ModuleInstantiation).getChild(i) } } diff --git a/ql/ql/src/codeql_ql/ast/internal/Module.qll b/ql/ql/src/codeql_ql/ast/internal/Module.qll index b487c98cc7e..d84f0b8122e 100644 --- a/ql/ql/src/codeql_ql/ast/internal/Module.qll +++ b/ql/ql/src/codeql_ql/ast/internal/Module.qll @@ -332,7 +332,19 @@ module ModConsistency { * } */ - query predicate noName(Module mod) { not exists(mod.getName()) } + query predicate noName(AstNode mod) { + mod instanceof Module and + not exists(mod.(Module).getName()) + or + mod instanceof ModuleExpr and + not exists(mod.(ModuleExpr).getName()) + } - query predicate nonUniqueName(Module mod) { count(mod.getName()) >= 2 } + query predicate nonUniqueName(AstNode mod) { + mod instanceof Module and + count(mod.(Module).getName()) >= 2 + or + mod instanceof ModuleExpr and + count(mod.(ModuleExpr).getName()) >= 2 + } } From f08f02ed66724fe55482746e5ade7f826ad64ddd Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Sun, 19 Jun 2022 20:38:16 +0200 Subject: [PATCH 051/505] use the explicit super type to resolve calls --- ql/ql/src/codeql_ql/ast/internal/Predicate.qll | 17 +++++++++++++---- ql/ql/test/callgraph/MultiResolve.qll | 16 ++++++++++++++++ ql/ql/test/callgraph/callgraph.expected | 3 ++- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/ql/ql/src/codeql_ql/ast/internal/Predicate.qll b/ql/ql/src/codeql_ql/ast/internal/Predicate.qll index b4a5eaa6574..60b52c3241f 100644 --- a/ql/ql/src/codeql_ql/ast/internal/Predicate.qll +++ b/ql/ql/src/codeql_ql/ast/internal/Predicate.qll @@ -95,15 +95,24 @@ private module Cached { ) or // super calls - and `this.method()` calls in charpreds. (Basically: in charpreds there is no difference between super and this.) - exists(AstNode sup, ClassType type, Type supertype | + exists(AstNode sup, Type supertype | sup instanceof Super or sup.(ThisAccess).getEnclosingPredicate() instanceof CharPred | mc.getBase() = sup and - sup.getEnclosingPredicate().getParent().(Class).getType() = type and - supertype in [type.getASuperType(), type.getAnInstanceofType()] and - p = supertype.getClassPredicate(mc.getMemberName(), mc.getNumberOfArguments()) + p = supertype.getClassPredicate(mc.getMemberName(), mc.getNumberOfArguments()) and + ( + // super.method() + not exists(mc.getSuperType()) and + exists(ClassType type | + sup.getEnclosingPredicate().getParent().(Class).getType() = type and + supertype in [type.getASuperType(), type.getAnInstanceofType()] + ) + or + // Class.super.method() + supertype = mc.getSuperType().getResolvedType() + ) ) } diff --git a/ql/ql/test/callgraph/MultiResolve.qll b/ql/ql/test/callgraph/MultiResolve.qll index 161d86077ad..aabb680709d 100644 --- a/ql/ql/test/callgraph/MultiResolve.qll +++ b/ql/ql/test/callgraph/MultiResolve.qll @@ -24,3 +24,19 @@ module MyMod { } } } + +class Super1 extends int { + Super1() { this = 42 } + + predicate foo() { any() } +} + +class Super2 extends int { + Super2() { this = 42 } + + predicate foo() { none() } +} + +class Sub extends Super1, Super2 { + override predicate foo() { Super1.super.foo() } // <- should resolve to Super1::foo() +} diff --git a/ql/ql/test/callgraph/callgraph.expected b/ql/ql/test/callgraph/callgraph.expected index b640afaecc6..52cd12bcb53 100644 --- a/ql/ql/test/callgraph/callgraph.expected +++ b/ql/ql/test/callgraph/callgraph.expected @@ -16,7 +16,8 @@ getTarget | Foo.qll:38:39:38:67 | MemberCall | file://:0:0:0:0 | regexpCapture | | MultiResolve.qll:14:25:14:35 | PredicateCall | MultiResolve.qll:1:1:4:1 | ClasslessPredicate foo | | MultiResolve.qll:14:25:14:35 | PredicateCall | MultiResolve.qll:12:1:12:24 | ClasslessPredicate myFoo | -| MultiResolve.qll:25:7:25:18 | PredicateCall | MultiResolve.qll:17:3:19:3 | ClasslessPredicate bar | +| MultiResolve.qll:23:7:23:18 | PredicateCall | MultiResolve.qll:17:3:17:27 | ClasslessPredicate bar | +| MultiResolve.qll:41:30:41:47 | MemberCall | MultiResolve.qll:31:3:31:27 | ClassPredicate foo | | Overrides.qll:8:30:8:39 | MemberCall | Overrides.qll:6:3:6:29 | ClassPredicate bar | | Overrides.qll:16:39:16:48 | MemberCall | Overrides.qll:6:3:6:29 | ClassPredicate bar | | Overrides.qll:16:39:16:48 | MemberCall | Overrides.qll:14:12:14:43 | ClassPredicate bar | From f8b451a514bcd55db4a5c3ae645171744fb64bdc Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Sun, 19 Jun 2022 22:38:09 +0200 Subject: [PATCH 052/505] get all calls to resolve to a unique predicate (within reason) --- ql/ql/src/codeql_ql/ast/internal/Module.qll | 3 +- .../src/codeql_ql/ast/internal/Predicate.qll | 46 +++++++++++++------ .../queries/diagnostics/EmptyConsistencies.ql | 2 + ql/ql/test/callgraph/MultiResolve.qll | 16 +++++++ ql/ql/test/callgraph/callgraph.expected | 5 +- 5 files changed, 54 insertions(+), 18 deletions(-) diff --git a/ql/ql/src/codeql_ql/ast/internal/Module.qll b/ql/ql/src/codeql_ql/ast/internal/Module.qll index d84f0b8122e..4b224ad4adf 100644 --- a/ql/ql/src/codeql_ql/ast/internal/Module.qll +++ b/ql/ql/src/codeql_ql/ast/internal/Module.qll @@ -229,7 +229,8 @@ private module Cached { pragma[noinline] private predicate resolveModuleRefHelper(TypeRef me, ContainerOrModule enclosing, string name) { enclosing = getEnclosingModule(me).getEnclosing*() and - name = [me.(ModuleExpr).getName(), me.(TypeExpr).getClassName()] + name = [me.(ModuleExpr).getName(), me.(TypeExpr).getClassName()] and + (not me instanceof ModuleExpr or not enclosing instanceof Folder_) // module expressions are not imports, so they can't resolve to a file (which is contained in a folder). } } diff --git a/ql/ql/src/codeql_ql/ast/internal/Predicate.qll b/ql/ql/src/codeql_ql/ast/internal/Predicate.qll index 60b52c3241f..e2da87b1904 100644 --- a/ql/ql/src/codeql_ql/ast/internal/Predicate.qll +++ b/ql/ql/src/codeql_ql/ast/internal/Predicate.qll @@ -88,7 +88,23 @@ private module Cached { ) } + /** + * Holds if `mc` is a `this.method()` call to a predicate defined in the same class. + * helps avoid spuriously resolving to predicates in super-classes. + */ + private predicate resolveSelfClassCalls(MemberCall mc, PredicateOrBuiltin p) { + exists(Class c | + mc.getBase() instanceof ThisAccess and + c = mc.getEnclosingPredicate().getParent() and + p = c.getClassPredicate(mc.getMemberName()) and + p.getArity() = mc.getNumberOfArguments() + ) + } + private predicate resolveMemberCall(MemberCall mc, PredicateOrBuiltin p) { + resolveSelfClassCalls(mc, p) + or + not resolveSelfClassCalls(mc, _) and exists(Type t | t = mc.getBase().getType() and p = t.getClassPredicate(mc.getMemberName(), mc.getNumberOfArguments()) @@ -188,20 +204,20 @@ module PredConsistency { c > 1 and resolvePredicateExpr(pe, p) } - // This can happen with parameterized modules - /* - * query predicate multipleResolveCall(Call call, int c, PredicateOrBuiltin p) { - * c = - * strictcount(PredicateOrBuiltin p0 | - * resolveCall(call, p0) and - * // aliases are expected to resolve to multiple. - * not exists(p0.(ClasslessPredicate).getAlias()) and - * // overridden predicates may have multiple targets - * not p0.(ClassPredicate).isOverride() - * ) and - * c > 1 and - * resolveCall(call, p) - * } - */ + query predicate multipleResolveCall(Call call, int c, PredicateOrBuiltin p) { + c = + strictcount(PredicateOrBuiltin p0 | + resolveCall(call, p0) and + // aliases are expected to resolve to multiple. + not exists(p0.(ClasslessPredicate).getAlias()) and + // overridden predicates may have multiple targets + not p0.(ClassPredicate).isOverride() and + not p0 instanceof Relation // <- DB relations resolve to both a relation and a predicate. + ) and + c > 1 and + resolveCall(call, p) and + // parameterized modules are expected to resolve to multiple. + not exists(Predicate sig | not exists(sig.getBody()) and resolveCall(call, sig)) } +} diff --git a/ql/ql/src/queries/diagnostics/EmptyConsistencies.ql b/ql/ql/src/queries/diagnostics/EmptyConsistencies.ql index 7f95fec935e..8cd89f7fd37 100644 --- a/ql/ql/src/queries/diagnostics/EmptyConsistencies.ql +++ b/ql/ql/src/queries/diagnostics/EmptyConsistencies.ql @@ -22,6 +22,8 @@ where or PredConsistency::noResolvePredicateExpr(node) and msg = "PredConsistency::noResolvePredicateExpr" or + PredConsistency::multipleResolveCall(node, _, _) and msg = "PredConsistency::multipleResolveCall" + or TypeConsistency::exprNoType(node) and msg = "TypeConsistency::exprNoType" or TypeConsistency::varDefNoType(node) and msg = "TypeConsistency::varDefNoType" diff --git a/ql/ql/test/callgraph/MultiResolve.qll b/ql/ql/test/callgraph/MultiResolve.qll index aabb680709d..dce88d01689 100644 --- a/ql/ql/test/callgraph/MultiResolve.qll +++ b/ql/ql/test/callgraph/MultiResolve.qll @@ -40,3 +40,19 @@ class Super2 extends int { class Sub extends Super1, Super2 { override predicate foo() { Super1.super.foo() } // <- should resolve to Super1::foo() } + +module Foo { + predicate foo() { any() } +} + +predicate test() { + Foo::foo() // <- should resolve to `foo` from the module above, and not from the `Foo.qll` file. +} + +class Sub2 extends Super1, Super2 { + override predicate foo() { Super2.super.foo() } // <- should resolve to Super2::foo() + + predicate test() { + this.foo() // <- should resolve to only the above `foo` predicate, but currently it resolves to that, and all the overrides [INCONSISTENCY] + } +} diff --git a/ql/ql/test/callgraph/callgraph.expected b/ql/ql/test/callgraph/callgraph.expected index 52cd12bcb53..4d7840383cf 100644 --- a/ql/ql/test/callgraph/callgraph.expected +++ b/ql/ql/test/callgraph/callgraph.expected @@ -18,10 +18,11 @@ getTarget | MultiResolve.qll:14:25:14:35 | PredicateCall | MultiResolve.qll:12:1:12:24 | ClasslessPredicate myFoo | | MultiResolve.qll:23:7:23:18 | PredicateCall | MultiResolve.qll:17:3:17:27 | ClasslessPredicate bar | | MultiResolve.qll:41:30:41:47 | MemberCall | MultiResolve.qll:31:3:31:27 | ClassPredicate foo | +| MultiResolve.qll:49:3:49:12 | PredicateCall | MultiResolve.qll:45:3:45:27 | ClasslessPredicate foo | +| MultiResolve.qll:53:30:53:47 | MemberCall | MultiResolve.qll:37:3:37:28 | ClassPredicate foo | +| MultiResolve.qll:56:5:56:14 | MemberCall | MultiResolve.qll:53:12:53:49 | ClassPredicate foo | | Overrides.qll:8:30:8:39 | MemberCall | Overrides.qll:6:3:6:29 | ClassPredicate bar | -| Overrides.qll:16:39:16:48 | MemberCall | Overrides.qll:6:3:6:29 | ClassPredicate bar | | Overrides.qll:16:39:16:48 | MemberCall | Overrides.qll:14:12:14:43 | ClassPredicate bar | -| Overrides.qll:24:39:24:48 | MemberCall | Overrides.qll:6:3:6:29 | ClassPredicate bar | | Overrides.qll:24:39:24:48 | MemberCall | Overrides.qll:22:12:22:44 | ClassPredicate bar | | Overrides.qll:28:3:28:9 | MemberCall | Overrides.qll:6:3:6:29 | ClassPredicate bar | | Overrides.qll:29:3:29:10 | MemberCall | Overrides.qll:8:3:8:41 | ClassPredicate baz | From 15f9e084d5962b1d3b5930d935574a8d2c8c7d77 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Sun, 19 Jun 2022 22:43:41 +0200 Subject: [PATCH 053/505] fix spurious resolved predicate expressions --- ql/ql/src/codeql_ql/ast/internal/Predicate.qll | 10 ++++++++-- ql/ql/src/queries/diagnostics/EmptyConsistencies.ql | 3 +++ ql/ql/test/callgraph/callgraph.expected | 1 - 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/ql/ql/src/codeql_ql/ast/internal/Predicate.qll b/ql/ql/src/codeql_ql/ast/internal/Predicate.qll index e2da87b1904..6b17216b9aa 100644 --- a/ql/ql/src/codeql_ql/ast/internal/Predicate.qll +++ b/ql/ql/src/codeql_ql/ast/internal/Predicate.qll @@ -49,7 +49,8 @@ private module Cached { m = pe.getQualifier().getResolvedModule() and public = true | - definesPredicate(m, pe.getName(), p.getArity(), p, public) + definesPredicate(m, pe.getName(), p.getArity(), p, public) and + p.getArity() = pe.getArity() ) } @@ -200,7 +201,12 @@ module PredConsistency { } query predicate multipleResolvePredicateExpr(PredicateExpr pe, int c, ClasslessPredicate p) { - c = strictcount(ClasslessPredicate p0 | resolvePredicateExpr(pe, p0)) and + c = + strictcount(ClasslessPredicate p0 | + resolvePredicateExpr(pe, p0) and + // aliases are expected to resolve to multiple. + not exists(p0.(ClasslessPredicate).getAlias()) + ) and c > 1 and resolvePredicateExpr(pe, p) } diff --git a/ql/ql/src/queries/diagnostics/EmptyConsistencies.ql b/ql/ql/src/queries/diagnostics/EmptyConsistencies.ql index 8cd89f7fd37..8691aa2168c 100644 --- a/ql/ql/src/queries/diagnostics/EmptyConsistencies.ql +++ b/ql/ql/src/queries/diagnostics/EmptyConsistencies.ql @@ -24,6 +24,9 @@ where or PredConsistency::multipleResolveCall(node, _, _) and msg = "PredConsistency::multipleResolveCall" or + PredConsistency::multipleResolvePredicateExpr(node, _, _) and + msg = "PredConsistency::multipleResolvePredicateExpr" + or TypeConsistency::exprNoType(node) and msg = "TypeConsistency::exprNoType" or TypeConsistency::varDefNoType(node) and msg = "TypeConsistency::varDefNoType" diff --git a/ql/ql/test/callgraph/callgraph.expected b/ql/ql/test/callgraph/callgraph.expected index 4d7840383cf..de11b0d658c 100644 --- a/ql/ql/test/callgraph/callgraph.expected +++ b/ql/ql/test/callgraph/callgraph.expected @@ -49,6 +49,5 @@ exprPredicate | Foo.qll:47:55:47:62 | predicate | Foo.qll:42:20:42:27 | NewTypeBranch MkRoot | | Foo.qll:47:65:47:70 | predicate | Foo.qll:44:9:44:56 | ClasslessPredicate edge | | MultiResolve.qll:12:19:12:23 | predicate | MultiResolve.qll:1:1:4:1 | ClasslessPredicate foo | -| MultiResolve.qll:12:19:12:23 | predicate | MultiResolve.qll:6:1:10:1 | ClasslessPredicate foo | | ParamModules.qll:4:18:4:25 | predicate | ParamModules.qll:2:13:2:36 | ClasslessPredicate fooSig | | ParamModules.qll:10:34:10:40 | predicate | ParamModules.qll:8:3:8:35 | ClasslessPredicate myFoo | From 6d3808bd899210863868c42b4f41369481bbd73f Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Sun, 19 Jun 2022 23:19:01 +0200 Subject: [PATCH 054/505] remove redundant cast --- ql/ql/src/codeql_ql/ast/internal/Predicate.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ql/ql/src/codeql_ql/ast/internal/Predicate.qll b/ql/ql/src/codeql_ql/ast/internal/Predicate.qll index 6b17216b9aa..d419534788b 100644 --- a/ql/ql/src/codeql_ql/ast/internal/Predicate.qll +++ b/ql/ql/src/codeql_ql/ast/internal/Predicate.qll @@ -205,7 +205,7 @@ module PredConsistency { strictcount(ClasslessPredicate p0 | resolvePredicateExpr(pe, p0) and // aliases are expected to resolve to multiple. - not exists(p0.(ClasslessPredicate).getAlias()) + not exists(p0.getAlias()) ) and c > 1 and resolvePredicateExpr(pe, p) From 7d62b9e13179c8712ad9aa0994de24fd75442e94 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 20 Jun 2022 12:12:57 +0200 Subject: [PATCH 055/505] move the pruning for module resolution of TypeExprs --- ql/ql/src/codeql_ql/ast/Ast.qll | 11 +---------- ql/ql/src/codeql_ql/ast/internal/Module.qll | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/ql/ql/src/codeql_ql/ast/Ast.qll b/ql/ql/src/codeql_ql/ast/Ast.qll index 3a0063aaa76..c411f287fbc 100644 --- a/ql/ql/src/codeql_ql/ast/Ast.qll +++ b/ql/ql/src/codeql_ql/ast/Ast.qll @@ -683,16 +683,7 @@ class TypeExpr extends TType, TypeRef { resolveTypeExpr(this, result) or // if it resolves to a module, - exists(FileOrModule mod | resolveModuleRef(this, mod) | result = mod.toType()) and - result instanceof ModuleType and - // we can get spurious results in some cases, so we restrict to where it is possible to have a module. - ( - // only possible if this is inside a moduleInstantiation. - this = any(ModuleExpr mod).getArgument(_).asType() - or - // or if it's a parameter to a parameterized module - this = any(SignatureExpr sig, Module mod | mod.hasParameter(_, _, sig) | sig).asType() - ) + exists(FileOrModule mod | resolveModuleRef(this, mod) | result = mod.toType()) } override AstNode getAChild(string pred) { diff --git a/ql/ql/src/codeql_ql/ast/internal/Module.qll b/ql/ql/src/codeql_ql/ast/internal/Module.qll index 4b224ad4adf..1ca459ac108 100644 --- a/ql/ql/src/codeql_ql/ast/internal/Module.qll +++ b/ql/ql/src/codeql_ql/ast/internal/Module.qll @@ -218,6 +218,20 @@ private module Cached { not exists(me.(ModuleExpr).getQualifier()) and exists(ContainerOrModule enclosing, string name | resolveModuleRefHelper(me, enclosing, name) | definesModule(enclosing, name, m, _) + ) and + ( + not me instanceof TypeExpr + or + // remove some spurious results that can happen with `TypeExpr` + me instanceof TypeExpr and + m instanceof Module_ and // TypeExpr can only resolve to a Module, and only in some scenarios + ( + // only possible if this is inside a moduleInstantiation. + me = any(ModuleExpr mod).getArgument(_).asType() + or + // or if it's a parameter to a parameterized module + me = any(SignatureExpr sig, Module mod | mod.hasParameter(_, _, sig) | sig).asType() + ) ) or exists(FileOrModule mid | From 9d6b1bf14255d07234924585eafbd4ff9a0ee672 Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Mon, 20 Jun 2022 10:24:56 -0700 Subject: [PATCH 056/505] Apply suggestions from code review Co-authored-by: James Fletcher <42464962+jf205@users.noreply.github.com> --- ...nalyzing-databases-with-the-codeql-cli.rst | 22 +++++-------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst b/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst index cac63f6d01f..e6ee13c7823 100644 --- a/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst +++ b/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst @@ -161,34 +161,22 @@ If a ``scope/name`` and ``path`` are specified, then the ``path`` cannot be absolute. It is considered relative to the root of the CodeQL pack. -The relevant commands are: - -* `codeql database analyze <../manual/database-analyze>`__. -* `codeql database run-queries <../manual/database-run-queries>`__. -* `codeql execute queries <../manual/execute-queries>`__. -* `codeql resolve queries <../manual/resolve-queries>`__. - For example:: - # Analyze a database using all queries in the experimental/Security folder within the codeql/cpp-queries - # CodeQL query pack. +To analyze a database using all queries in the `experimental/Security` folder within the `codeql/cpp-queries` CodeQL pack you can use:: + codeql database analyze --format=sarif-latest --output=results \ codeql/cpp-queries:experimental/Security - # Analyse using only the RedundantNullCheckParam.ql query in the codeql/cpp-queries CodeQL query pack. +To run the `RedundantNullCheckParam.ql` query in the `codeql/cpp-queries` CodeQL pack use:: + codeql database analyze --format=sarif-latest --output=results \ 'codeql/cpp-queries:experimental/Likely Bugs/RedundantNullCheckParam.ql' - # Analyse using the cpp-security-and-quality.qls query suite in the codeql/cpp-queries CodeQL query pack. - codeql database analyze --format=sarif-latest --output=results \ - 'codeql/cpp-queries:codeql-suites/cpp-security-and-quality.qls' +To analyze your database using the `cpp-security-and-quality.qls` query suite from a version of the `codeql/cpp-queries` CodeQL pack that is >= 0.0.3 and < 0.1.0 (the highest compatible version will be chosen) you can use:: - # Analyse using the cpp-security-and-quality.qls query suite from a version of the codeql/cpp-queries pack - # that is >= 0.0.3 and < 0.1.0 (the highest compatible version will be chosen). - # All valid semver ranges are allowed. See https://docs.npmjs.com/cli/v6/using-npm/semver#ranges codeql database analyze --format=sarif-latest --output=results \ 'codeql/cpp-queries@~0.0.3:codeql-suites/cpp-security-and-quality.qls' - For more information about CodeQL packs, see :doc:`About CodeQL Packs `. Running query suites From 767b0cfdfb85b1f6fc5327af6091a990fc8f44fb Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Fri, 29 Apr 2022 11:45:10 -0400 Subject: [PATCH 057/505] Revert "Merge pull request #8933 from MathiasVP/revert-globals" This reverts commit 2517371a37e5da44e5f19903df9d543649b6dca3, reversing changes made to db856798b94e9d02db24273b036a84723ae776f7. --- cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll | 3 + .../cpp/ir/implementation/IRConfiguration.qll | 4 +- .../ir/implementation/aliased_ssa/IRBlock.qll | 2 +- .../aliased_ssa/Instruction.qll | 2 +- .../ir/implementation/aliased_ssa/PrintIR.qll | 8 +- .../internal/IRFunctionBase.qll | 17 ++- .../cpp/ir/implementation/raw/IRBlock.qll | 2 +- .../cpp/ir/implementation/raw/Instruction.qll | 2 +- .../cpp/ir/implementation/raw/PrintIR.qll | 8 +- .../raw/internal/IRConstruction.qll | 11 +- .../raw/internal/TranslatedCall.qll | 11 +- .../raw/internal/TranslatedElement.qll | 22 +++- .../raw/internal/TranslatedExpr.qll | 11 +- .../raw/internal/TranslatedFunction.qll | 2 +- .../raw/internal/TranslatedGlobalVar.qll | 104 ++++++++++++++++++ .../raw/internal/TranslatedInitialization.qll | 16 ++- .../implementation/unaliased_ssa/IRBlock.qll | 2 +- .../unaliased_ssa/Instruction.qll | 2 +- .../implementation/unaliased_ssa/PrintIR.qll | 8 +- .../code/cpp/ir/internal/IRCppLanguage.qll | 4 + .../dataflow-ir-consistency.expected | 16 +++ .../test/library-tests/ir/ir/PrintConfig.qll | 9 +- cpp/ql/test/library-tests/ir/ir/ir.cpp | 12 ++ .../ir/ir/operand_locations.expected | 75 +++++++++++++ .../test/library-tests/ir/ir/raw_ir.expected | 104 ++++++++++++++++++ cpp/ql/test/library-tests/ir/ir/raw_ir.ql | 2 +- .../ir/ssa/aliased_ssa_ir.expected | 2 + .../ir/ssa/aliased_ssa_ir_unsound.expected | 2 + .../ir/ssa/unaliased_ssa_ir.expected | 2 + .../ir/ssa/unaliased_ssa_ir_unsound.expected | 2 + .../aliased_ssa_consistency.expected | 4 + .../dataflow-ir-consistency.expected | 46 ++++++++ .../diff_ir_expr.expected | 3 - .../GlobalValueNumbering/ir_gvn.expected | 55 +++++++++ .../ir/implementation/IRConfiguration.qll | 4 +- .../internal/IRFunctionBase.qll | 17 ++- .../ir/implementation/raw/IRBlock.qll | 2 +- .../ir/implementation/raw/Instruction.qll | 2 +- .../ir/implementation/raw/PrintIR.qll | 8 +- .../raw/internal/IRConstruction.qll | 3 + .../implementation/unaliased_ssa/IRBlock.qll | 2 +- .../unaliased_ssa/Instruction.qll | 2 +- .../implementation/unaliased_ssa/PrintIR.qll | 8 +- .../ir/internal/IRCSharpLanguage.qll | 6 + 44 files changed, 553 insertions(+), 76 deletions(-) create mode 100644 cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedGlobalVar.qll diff --git a/cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll b/cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll index 14399078231..55a59cc9588 100644 --- a/cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll +++ b/cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll @@ -49,6 +49,9 @@ class Expr extends StmtParent, @expr { /** Gets the enclosing variable of this expression, if any. */ Variable getEnclosingVariable() { result = exprEnclosingElement(this) } + /** Gets the enclosing variable or function of this expression. */ + Declaration getEnclosingDeclaration() { result = exprEnclosingElement(this) } + /** Gets a child of this expression. */ Expr getAChild() { exists(int n | result = this.getChild(n)) } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/IRConfiguration.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/IRConfiguration.qll index 37ac2fccdd9..90cdb9e0f5f 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/IRConfiguration.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/IRConfiguration.qll @@ -16,7 +16,7 @@ class IRConfiguration extends TIRConfiguration { /** * Holds if IR should be created for function `func`. By default, holds for all functions. */ - predicate shouldCreateIRForFunction(Language::Function func) { any() } + predicate shouldCreateIRForFunction(Language::Declaration func) { any() } /** * Holds if the strings used as part of an IR dump should be generated for function `func`. @@ -25,7 +25,7 @@ class IRConfiguration extends TIRConfiguration { * of debug strings for IR that will not be dumped. We still generate the actual IR for these * functions, however, to preserve the results of any interprocedural analysis. */ - predicate shouldEvaluateDebugStringsForFunction(Language::Function func) { any() } + predicate shouldEvaluateDebugStringsForFunction(Language::Declaration func) { any() } } private newtype TIREscapeAnalysisConfiguration = MkIREscapeAnalysisConfiguration() diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll index bac7634cbd0..78008a6c69b 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll @@ -97,7 +97,7 @@ class IRBlockBase extends TIRBlock { /** * Gets the `Function` that contains this block. */ - final Language::Function getEnclosingFunction() { + final Language::Declaration getEnclosingFunction() { result = getFirstInstruction(this).getEnclosingFunction() } } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll index e5a908bbf9a..8e863ddf635 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll @@ -194,7 +194,7 @@ class Instruction extends Construction::TStageInstruction { /** * Gets the function that contains this instruction. */ - final Language::Function getEnclosingFunction() { + final Language::Declaration getEnclosingFunction() { result = this.getEnclosingIRFunction().getFunction() } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll index 59dadee7154..53cdc75512b 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll @@ -26,20 +26,20 @@ class PrintIRConfiguration extends TPrintIRConfiguration { * Holds if the IR for `func` should be printed. By default, holds for all * functions. */ - predicate shouldPrintFunction(Language::Function func) { any() } + predicate shouldPrintFunction(Language::Declaration decl) { any() } } /** * Override of `IRConfiguration` to only evaluate debug strings for the functions that are to be dumped. */ private class FilteredIRConfiguration extends IRConfiguration { - override predicate shouldEvaluateDebugStringsForFunction(Language::Function func) { + override predicate shouldEvaluateDebugStringsForFunction(Language::Declaration func) { shouldPrintFunction(func) } } -private predicate shouldPrintFunction(Language::Function func) { - exists(PrintIRConfiguration config | config.shouldPrintFunction(func)) +private predicate shouldPrintFunction(Language::Declaration decl) { + exists(PrintIRConfiguration config | config.shouldPrintFunction(decl)) } private string getAdditionalInstructionProperty(Instruction instr, string key) { diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll index 60895ce3d26..576b4f9adf1 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll @@ -5,23 +5,28 @@ private import IRFunctionBaseInternal private newtype TIRFunction = - MkIRFunction(Language::Function func) { IRConstruction::Raw::functionHasIR(func) } + TFunctionIRFunction(Language::Function func) { IRConstruction::Raw::functionHasIR(func) } or + TVarInitIRFunction(Language::GlobalVariable var) { IRConstruction::Raw::varHasIRFunc(var) } /** * The IR for a function. This base class contains only the predicates that are the same between all * phases of the IR. Each instantiation of `IRFunction` extends this class. */ class IRFunctionBase extends TIRFunction { - Language::Function func; + Language::Declaration decl; - IRFunctionBase() { this = MkIRFunction(func) } + IRFunctionBase() { + this = TFunctionIRFunction(decl) + or + this = TVarInitIRFunction(decl) + } /** Gets a textual representation of this element. */ - final string toString() { result = "IR: " + func.toString() } + final string toString() { result = "IR: " + decl.toString() } /** Gets the function whose IR is represented. */ - final Language::Function getFunction() { result = func } + final Language::Declaration getFunction() { result = decl } /** Gets the location of the function. */ - final Language::Location getLocation() { result = func.getLocation() } + final Language::Location getLocation() { result = decl.getLocation() } } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRBlock.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRBlock.qll index bac7634cbd0..78008a6c69b 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRBlock.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRBlock.qll @@ -97,7 +97,7 @@ class IRBlockBase extends TIRBlock { /** * Gets the `Function` that contains this block. */ - final Language::Function getEnclosingFunction() { + final Language::Declaration getEnclosingFunction() { result = getFirstInstruction(this).getEnclosingFunction() } } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/Instruction.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/Instruction.qll index e5a908bbf9a..8e863ddf635 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/Instruction.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/Instruction.qll @@ -194,7 +194,7 @@ class Instruction extends Construction::TStageInstruction { /** * Gets the function that contains this instruction. */ - final Language::Function getEnclosingFunction() { + final Language::Declaration getEnclosingFunction() { result = this.getEnclosingIRFunction().getFunction() } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/PrintIR.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/PrintIR.qll index 59dadee7154..53cdc75512b 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/PrintIR.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/PrintIR.qll @@ -26,20 +26,20 @@ class PrintIRConfiguration extends TPrintIRConfiguration { * Holds if the IR for `func` should be printed. By default, holds for all * functions. */ - predicate shouldPrintFunction(Language::Function func) { any() } + predicate shouldPrintFunction(Language::Declaration decl) { any() } } /** * Override of `IRConfiguration` to only evaluate debug strings for the functions that are to be dumped. */ private class FilteredIRConfiguration extends IRConfiguration { - override predicate shouldEvaluateDebugStringsForFunction(Language::Function func) { + override predicate shouldEvaluateDebugStringsForFunction(Language::Declaration func) { shouldPrintFunction(func) } } -private predicate shouldPrintFunction(Language::Function func) { - exists(PrintIRConfiguration config | config.shouldPrintFunction(func)) +private predicate shouldPrintFunction(Language::Declaration decl) { + exists(PrintIRConfiguration config | config.shouldPrintFunction(decl)) } private string getAdditionalInstructionProperty(Instruction instr, string key) { diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll index 94bfc53875f..ddd9ab50635 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll @@ -35,6 +35,9 @@ module Raw { cached predicate functionHasIR(Function func) { exists(getTranslatedFunction(func)) } + cached + predicate varHasIRFunc(GlobalOrNamespaceVariable var) { any() } // TODO: restrict? + cached predicate hasInstruction(TranslatedElement element, InstructionTag tag) { element.hasInstruction(_, tag, _) @@ -46,18 +49,18 @@ module Raw { } cached - predicate hasTempVariable(Function func, Locatable ast, TempVariableTag tag, CppType type) { + predicate hasTempVariable(Declaration decl, Locatable ast, TempVariableTag tag, CppType type) { exists(TranslatedElement element | element.getAst() = ast and - func = element.getFunction() and + decl = element.getFunction() and element.hasTempVariable(tag, type) ) } cached - predicate hasStringLiteral(Function func, Locatable ast, CppType type, StringLiteral literal) { + predicate hasStringLiteral(Declaration decl, Locatable ast, CppType type, StringLiteral literal) { literal = ast and - literal.getEnclosingFunction() = func and + literal.getEnclosingDeclaration() = decl and getTypeForPRValue(literal.getType()) = type } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCall.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCall.qll index 66c601736af..f8960cd205d 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCall.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCall.qll @@ -180,7 +180,7 @@ abstract class TranslatedSideEffects extends TranslatedElement { /** DEPRECATED: Alias for getAst */ deprecated override Locatable getAST() { result = getAst() } - final override Function getFunction() { result = getExpr().getEnclosingFunction() } + final override Declaration getFunction() { result = getExpr().getEnclosingDeclaration() } final override TranslatedElement getChild(int i) { result = @@ -375,7 +375,7 @@ abstract class TranslatedSideEffect extends TranslatedElement { kind instanceof GotoEdge } - final override Function getFunction() { result = getParent().getFunction() } + final override Declaration getFunction() { result = getParent().getFunction() } final override Instruction getPrimaryInstructionForSideEffect(InstructionTag tag) { tag = OnlyInstructionTag() and @@ -436,13 +436,6 @@ abstract class TranslatedArgumentSideEffect extends TranslatedSideEffect { result = index } - /** - * Gets the `TranslatedFunction` containing this expression. - */ - final TranslatedFunction getEnclosingFunction() { - result = getTranslatedFunction(call.getEnclosingFunction()) - } - final override predicate sideEffectInstruction(Opcode opcode, CppType type) { opcode = sideEffectOpcode and ( diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll index d11d718e215..8c53fe086a8 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll @@ -67,7 +67,8 @@ private predicate ignoreExprAndDescendants(Expr expr) { exists(Initializer init, StaticStorageDurationVariable var | init = var.getInitializer() and not var.hasDynamicInitialization() and - expr = init.getExpr().getFullyConverted() + expr = init.getExpr().getFullyConverted() and + not var instanceof GlobalOrNamespaceVariable ) or // Ignore descendants of `__assume` expressions, since we translated these to `NoOp`. @@ -117,7 +118,8 @@ private predicate ignoreExprOnly(Expr expr) { // should not be translated. exists(NewOrNewArrayExpr new | expr = new.getAllocatorCall().getArgument(0)) or - not translateFunction(expr.getEnclosingFunction()) + not translateFunction(expr.getEnclosingFunction()) and + not expr.getEnclosingVariable() instanceof GlobalOrNamespaceVariable or // We do not yet translate destructors properly, so for now we ignore the // destructor call. We do, however, translate the expression being @@ -662,7 +664,8 @@ newtype TTranslatedElement = opcode = getASideEffectOpcode(call, -1) } or // The side effect that initializes newly-allocated memory. - TTranslatedAllocationSideEffect(AllocationExpr expr) { not ignoreSideEffects(expr) } + TTranslatedAllocationSideEffect(AllocationExpr expr) { not ignoreSideEffects(expr) } or + TTranslatedGlobalOrNamespaceVarInit(GlobalOrNamespaceVariable var) { var.hasInitializer() } /** * Gets the index of the first explicitly initialized element in `initList` @@ -792,7 +795,7 @@ abstract class TranslatedElement extends TTranslatedElement { /** * Gets the `Function` that contains this element. */ - abstract Function getFunction(); + abstract Declaration getFunction(); /** * Gets the successor instruction of the instruction that was generated by @@ -942,3 +945,14 @@ abstract class TranslatedElement extends TTranslatedElement { */ final TranslatedElement getParent() { result.getAChild() = this } } + +/** + * Represents the IR translation of a root element, either a function or a global variable. + */ +abstract class TranslatedRootElement extends TranslatedElement { + TranslatedRootElement() { + this instanceof TTranslatedFunction + or + this instanceof TTranslatedGlobalOrNamespaceVarInit + } +} diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll index c81b7c5943a..5148122be05 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll @@ -12,6 +12,7 @@ private import TranslatedElement private import TranslatedFunction private import TranslatedInitialization private import TranslatedStmt +private import TranslatedGlobalVar import TranslatedCall /** @@ -78,7 +79,7 @@ abstract class TranslatedExpr extends TranslatedElement { /** DEPRECATED: Alias for getAst */ deprecated override Locatable getAST() { result = this.getAst() } - final override Function getFunction() { result = expr.getEnclosingFunction() } + final override Declaration getFunction() { result = expr.getEnclosingDeclaration() } /** * Gets the expression from which this `TranslatedExpr` is generated. @@ -88,8 +89,10 @@ abstract class TranslatedExpr extends TranslatedElement { /** * Gets the `TranslatedFunction` containing this expression. */ - final TranslatedFunction getEnclosingFunction() { + final TranslatedRootElement getEnclosingFunction() { result = getTranslatedFunction(expr.getEnclosingFunction()) + or + result = getTranslatedVarInit(expr.getEnclosingVariable()) } } @@ -787,7 +790,7 @@ class TranslatedThisExpr extends TranslatedNonConstantExpr { override IRVariable getInstructionVariable(InstructionTag tag) { tag = ThisAddressTag() and - result = this.getEnclosingFunction().getThisVariable() + result = this.getEnclosingFunction().(TranslatedFunction).getThisVariable() } } @@ -2522,7 +2525,7 @@ class TranslatedVarArgsStart extends TranslatedNonConstantExpr { final override IRVariable getInstructionVariable(InstructionTag tag) { tag = VarArgsStartEllipsisAddressTag() and - result = this.getEnclosingFunction().getEllipsisVariable() + result = this.getEnclosingFunction().(TranslatedFunction).getEllipsisVariable() } final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) { diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll index 0f781cb2244..b4746ae58de 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll @@ -58,7 +58,7 @@ predicate hasReturnValue(Function func) { not func.getUnspecifiedType() instance * Represents the IR translation of a function. This is the root elements for * all other elements associated with this function. */ -class TranslatedFunction extends TranslatedElement, TTranslatedFunction { +class TranslatedFunction extends TranslatedRootElement, TTranslatedFunction { Function func; TranslatedFunction() { this = TTranslatedFunction(func) } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedGlobalVar.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedGlobalVar.qll new file mode 100644 index 00000000000..abc175b7040 --- /dev/null +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedGlobalVar.qll @@ -0,0 +1,104 @@ +import semmle.code.cpp.ir.implementation.raw.internal.TranslatedElement +private import cpp +private import semmle.code.cpp.ir.implementation.IRType +private import semmle.code.cpp.ir.implementation.Opcode +private import semmle.code.cpp.ir.implementation.internal.OperandTag +private import semmle.code.cpp.ir.internal.CppType +private import TranslatedInitialization +private import InstructionTag + +class TranslatedGlobalOrNamespaceVarInit extends TranslatedRootElement, + TTranslatedGlobalOrNamespaceVarInit, InitializationContext { + GlobalOrNamespaceVariable var; + + TranslatedGlobalOrNamespaceVarInit() { this = TTranslatedGlobalOrNamespaceVarInit(var) } + + override string toString() { result = var.toString() } + + final override GlobalOrNamespaceVariable getAst() { result = var } + + final override Declaration getFunction() { result = var } + + final Location getLocation() { result = var.getLocation() } + + override Instruction getFirstInstruction() { result = this.getInstruction(EnterFunctionTag()) } + + override TranslatedElement getChild(int n) { + n = 1 and + result = getTranslatedInitialization(var.getInitializer().getExpr().getFullyConverted()) + } + + override predicate hasInstruction(Opcode op, InstructionTag tag, CppType type) { + op instanceof Opcode::EnterFunction and + tag = EnterFunctionTag() and + type = getVoidType() + or + op instanceof Opcode::AliasedDefinition and + tag = AliasedDefinitionTag() and + type = getUnknownType() + or + op instanceof Opcode::VariableAddress and + tag = InitializerVariableAddressTag() and + type = getTypeForGLValue(var.getType()) + or + op instanceof Opcode::ReturnVoid and + tag = ReturnTag() and + type = getVoidType() + or + op instanceof Opcode::AliasedUse and + tag = AliasedUseTag() and + type = getVoidType() + or + op instanceof Opcode::ExitFunction and + tag = ExitFunctionTag() and + type = getVoidType() + } + + override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { + kind instanceof GotoEdge and + ( + tag = EnterFunctionTag() and + result = getInstruction(AliasedDefinitionTag()) + or + tag = AliasedDefinitionTag() and + result = getInstruction(InitializerVariableAddressTag()) + or + tag = InitializerVariableAddressTag() and + result = getChild(1).getFirstInstruction() + or + tag = ReturnTag() and + result = getInstruction(AliasedUseTag()) + or + tag = AliasedUseTag() and + result = getInstruction(ExitFunctionTag()) + ) + } + + override Instruction getChildSuccessor(TranslatedElement child) { + child = getChild(1) and + result = getInstruction(ReturnTag()) + } + + final override CppType getInstructionMemoryOperandType( + InstructionTag tag, TypedOperandTag operandTag + ) { + tag = AliasedUseTag() and + operandTag instanceof SideEffectOperandTag and + result = getUnknownType() + } + + override IRUserVariable getInstructionVariable(InstructionTag tag) { + tag = InitializerVariableAddressTag() and + result.getVariable() = var + } + + override Instruction getTargetAddress() { + result = getInstruction(InitializerVariableAddressTag()) + } + + override Type getTargetType() { result = var.getUnspecifiedType() } +} + +TranslatedGlobalOrNamespaceVarInit getTranslatedVarInit(GlobalOrNamespaceVariable var) { + result.getAst() = var +} diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll index 1a9d7ad9d70..b800405a73b 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll @@ -137,7 +137,10 @@ abstract class TranslatedInitialization extends TranslatedElement, TTranslatedIn final override string toString() { result = "init: " + expr.toString() } - final override Function getFunction() { result = expr.getEnclosingFunction() } + final override Declaration getFunction() { + result = expr.getEnclosingFunction() or + result = expr.getEnclosingVariable().(GlobalOrNamespaceVariable) + } final override Locatable getAst() { result = expr } @@ -486,7 +489,10 @@ abstract class TranslatedFieldInitialization extends TranslatedElement { /** DEPRECATED: Alias for getAst */ deprecated override Locatable getAST() { result = getAst() } - final override Function getFunction() { result = ast.getEnclosingFunction() } + final override Declaration getFunction() { + result = ast.getEnclosingFunction() or + result = ast.getEnclosingVariable().(GlobalOrNamespaceVariable) + } final override Instruction getFirstInstruction() { result = getInstruction(getFieldAddressTag()) } @@ -633,7 +639,11 @@ abstract class TranslatedElementInitialization extends TranslatedElement { /** DEPRECATED: Alias for getAst */ deprecated override Locatable getAST() { result = getAst() } - final override Function getFunction() { result = initList.getEnclosingFunction() } + final override Declaration getFunction() { + result = initList.getEnclosingFunction() + or + result = initList.getEnclosingVariable().(GlobalOrNamespaceVariable) + } final override Instruction getFirstInstruction() { result = getInstruction(getElementIndexTag()) } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll index bac7634cbd0..78008a6c69b 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll @@ -97,7 +97,7 @@ class IRBlockBase extends TIRBlock { /** * Gets the `Function` that contains this block. */ - final Language::Function getEnclosingFunction() { + final Language::Declaration getEnclosingFunction() { result = getFirstInstruction(this).getEnclosingFunction() } } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll index e5a908bbf9a..8e863ddf635 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll @@ -194,7 +194,7 @@ class Instruction extends Construction::TStageInstruction { /** * Gets the function that contains this instruction. */ - final Language::Function getEnclosingFunction() { + final Language::Declaration getEnclosingFunction() { result = this.getEnclosingIRFunction().getFunction() } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll index 59dadee7154..53cdc75512b 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll @@ -26,20 +26,20 @@ class PrintIRConfiguration extends TPrintIRConfiguration { * Holds if the IR for `func` should be printed. By default, holds for all * functions. */ - predicate shouldPrintFunction(Language::Function func) { any() } + predicate shouldPrintFunction(Language::Declaration decl) { any() } } /** * Override of `IRConfiguration` to only evaluate debug strings for the functions that are to be dumped. */ private class FilteredIRConfiguration extends IRConfiguration { - override predicate shouldEvaluateDebugStringsForFunction(Language::Function func) { + override predicate shouldEvaluateDebugStringsForFunction(Language::Declaration func) { shouldPrintFunction(func) } } -private predicate shouldPrintFunction(Language::Function func) { - exists(PrintIRConfiguration config | config.shouldPrintFunction(func)) +private predicate shouldPrintFunction(Language::Declaration decl) { + exists(PrintIRConfiguration config | config.shouldPrintFunction(decl)) } private string getAdditionalInstructionProperty(Instruction instr, string key) { diff --git a/cpp/ql/lib/semmle/code/cpp/ir/internal/IRCppLanguage.qll b/cpp/ql/lib/semmle/code/cpp/ir/internal/IRCppLanguage.qll index f047d6c4753..46e3e6dec1c 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/internal/IRCppLanguage.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/internal/IRCppLanguage.qll @@ -50,12 +50,16 @@ class AutomaticVariable = Cpp::StackVariable; class StaticVariable = Cpp::Variable; +class GlobalVariable = Cpp::GlobalOrNamespaceVariable; + class Parameter = Cpp::Parameter; class Field = Cpp::Field; class BuiltInOperation = Cpp::BuiltInOperation; +class Declaration = Cpp::Declaration; + // TODO: Remove necessity for these. class Expr = Cpp::Expr; diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected index f04db828a25..056d4b6bfe3 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected @@ -1,4 +1,18 @@ uniqueEnclosingCallable +| globals.cpp:9:5:9:19 | Address | Node should have one enclosing callable but has 0. | +| globals.cpp:9:5:9:19 | VariableAddress | Node should have one enclosing callable but has 0. | +| globals.cpp:9:5:9:19 | VariableAddress [post update] | Node should have one enclosing callable but has 0. | +| globals.cpp:9:23:9:23 | 0 | Node should have one enclosing callable but has 0. | +| globals.cpp:9:23:9:23 | ChiPartial | Node should have one enclosing callable but has 0. | +| globals.cpp:9:23:9:23 | Store | Node should have one enclosing callable but has 0. | +| globals.cpp:9:23:9:23 | StoreValue | Node should have one enclosing callable but has 0. | +| globals.cpp:16:12:16:26 | Address | Node should have one enclosing callable but has 0. | +| globals.cpp:16:12:16:26 | VariableAddress | Node should have one enclosing callable but has 0. | +| globals.cpp:16:12:16:26 | VariableAddress [post update] | Node should have one enclosing callable but has 0. | +| globals.cpp:16:30:16:30 | 0 | Node should have one enclosing callable but has 0. | +| globals.cpp:16:30:16:30 | ChiPartial | Node should have one enclosing callable but has 0. | +| globals.cpp:16:30:16:30 | Store | Node should have one enclosing callable but has 0. | +| globals.cpp:16:30:16:30 | StoreValue | Node should have one enclosing callable but has 0. | uniqueType uniqueNodeLocation | BarrierGuard.cpp:2:11:2:13 | (unnamed parameter 0) | Node should have one location but has 6. | @@ -199,7 +213,9 @@ postWithInFlow | example.c:28:22:28:25 | & ... [post update] | PostUpdateNode should not be the target of local flow. | | example.c:28:23:28:25 | pos [post update] | PostUpdateNode should not be the target of local flow. | | globals.cpp:5:9:5:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. | +| globals.cpp:9:5:9:19 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. | | globals.cpp:13:5:13:19 | flowTestGlobal1 [post update] | PostUpdateNode should not be the target of local flow. | +| globals.cpp:16:12:16:26 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. | | globals.cpp:23:5:23:19 | flowTestGlobal2 [post update] | PostUpdateNode should not be the target of local flow. | | lambdas.cpp:8:6:8:6 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. | | lambdas.cpp:9:6:9:6 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. | diff --git a/cpp/ql/test/library-tests/ir/ir/PrintConfig.qll b/cpp/ql/test/library-tests/ir/ir/PrintConfig.qll index ccf243386fe..a9167597691 100644 --- a/cpp/ql/test/library-tests/ir/ir/PrintConfig.qll +++ b/cpp/ql/test/library-tests/ir/ir/PrintConfig.qll @@ -12,4 +12,11 @@ predicate locationIsInStandardHeaders(Location loc) { * * This predicate excludes functions defined in standard headers. */ -predicate shouldDumpFunction(Function func) { not locationIsInStandardHeaders(func.getLocation()) } +predicate shouldDumpFunction(Declaration decl) { + not locationIsInStandardHeaders(decl.getLocation()) and + ( + not decl instanceof Variable + or + decl.(GlobalOrNamespaceVariable).hasInitializer() + ) +} diff --git a/cpp/ql/test/library-tests/ir/ir/ir.cpp b/cpp/ql/test/library-tests/ir/ir/ir.cpp index e85c5f1b505..a07036da2ba 100644 --- a/cpp/ql/test/library-tests/ir/ir/ir.cpp +++ b/cpp/ql/test/library-tests/ir/ir/ir.cpp @@ -1816,4 +1816,16 @@ void switch_initialization(int x) { } } +int global_1; + +int global_2 = 1; + +const int global_3 = 2; + +constructor_only global_4(1); + +constructor_only global_5 = constructor_only(2); + +char *global_string = "global string"; + // semmle-extractor-options: -std=c++17 --clang diff --git a/cpp/ql/test/library-tests/ir/ir/operand_locations.expected b/cpp/ql/test/library-tests/ir/ir/operand_locations.expected index 1581085efc6..e0e04301e6f 100644 --- a/cpp/ql/test/library-tests/ir/ir/operand_locations.expected +++ b/cpp/ql/test/library-tests/ir/ir/operand_locations.expected @@ -4743,6 +4743,16 @@ | ir.cpp:1034:6:1034:20 | ChiTotal | total:m1034_2 | | ir.cpp:1034:6:1034:20 | SideEffect | m1034_3 | | ir.cpp:1035:15:1035:15 | Address | &:r1035_1 | +| ir.cpp:1038:6:1038:8 | Address | &:r1038_3 | +| ir.cpp:1038:6:1038:8 | SideEffect | ~m1038_9 | +| ir.cpp:1038:12:1038:18 | Address | &:r1038_4 | +| ir.cpp:1038:12:1038:18 | Address | &:r1038_4 | +| ir.cpp:1038:12:1038:18 | ChiPartial | partial:m1038_5 | +| ir.cpp:1038:12:1038:18 | ChiPartial | partial:m1038_8 | +| ir.cpp:1038:12:1038:18 | ChiTotal | total:m1038_2 | +| ir.cpp:1038:12:1038:18 | ChiTotal | total:m1038_6 | +| ir.cpp:1038:12:1038:18 | Load | ~m1038_6 | +| ir.cpp:1038:12:1038:18 | StoreValue | r1038_7 | | ir.cpp:1038:14:1038:14 | Address | &:r1038_5 | | ir.cpp:1038:14:1038:14 | Address | &:r1038_5 | | ir.cpp:1038:14:1038:14 | Address | &:r1038_5 | @@ -8457,6 +8467,43 @@ | ir.cpp:1815:14:1815:15 | Address | &:r1815_1 | | ir.cpp:1815:14:1815:15 | Load | m1813_4 | | ir.cpp:1815:14:1815:15 | Right | r1815_2 | +| ir.cpp:1821:5:1821:12 | Address | &:r1821_3 | +| ir.cpp:1821:5:1821:12 | SideEffect | ~m1821_6 | +| ir.cpp:1821:16:1821:16 | ChiPartial | partial:m1821_5 | +| ir.cpp:1821:16:1821:16 | ChiTotal | total:m1821_2 | +| ir.cpp:1821:16:1821:16 | StoreValue | r1821_4 | +| ir.cpp:1823:11:1823:18 | Address | &:r1823_3 | +| ir.cpp:1823:11:1823:18 | SideEffect | ~m1823_6 | +| ir.cpp:1823:22:1823:22 | ChiPartial | partial:m1823_5 | +| ir.cpp:1823:22:1823:22 | ChiTotal | total:m1823_2 | +| ir.cpp:1823:22:1823:22 | StoreValue | r1823_4 | +| ir.cpp:1825:18:1825:25 | Address | &:r1825_3 | +| ir.cpp:1825:18:1825:25 | Arg(this) | this:r1825_3 | +| ir.cpp:1825:18:1825:25 | SideEffect | ~m1825_10 | +| ir.cpp:1825:27:1825:27 | Arg(0) | 0:r1825_5 | +| ir.cpp:1825:27:1825:28 | CallTarget | func:r1825_4 | +| ir.cpp:1825:27:1825:28 | ChiPartial | partial:m1825_7 | +| ir.cpp:1825:27:1825:28 | ChiPartial | partial:m1825_9 | +| ir.cpp:1825:27:1825:28 | ChiTotal | total:m1825_2 | +| ir.cpp:1825:27:1825:28 | ChiTotal | total:m1825_8 | +| ir.cpp:1825:27:1825:28 | SideEffect | ~m1825_2 | +| ir.cpp:1827:18:1827:25 | Address | &:r1827_3 | +| ir.cpp:1827:18:1827:25 | Arg(this) | this:r1827_3 | +| ir.cpp:1827:18:1827:25 | SideEffect | ~m1827_10 | +| ir.cpp:1827:28:1827:47 | CallTarget | func:r1827_4 | +| ir.cpp:1827:28:1827:47 | ChiPartial | partial:m1827_7 | +| ir.cpp:1827:28:1827:47 | ChiPartial | partial:m1827_9 | +| ir.cpp:1827:28:1827:47 | ChiTotal | total:m1827_2 | +| ir.cpp:1827:28:1827:47 | ChiTotal | total:m1827_8 | +| ir.cpp:1827:28:1827:47 | SideEffect | ~m1827_2 | +| ir.cpp:1827:46:1827:46 | Arg(0) | 0:r1827_5 | +| ir.cpp:1829:7:1829:19 | Address | &:r1829_3 | +| ir.cpp:1829:7:1829:19 | SideEffect | ~m1829_8 | +| ir.cpp:1829:23:1829:37 | ChiPartial | partial:m1829_7 | +| ir.cpp:1829:23:1829:37 | ChiTotal | total:m1829_2 | +| ir.cpp:1829:23:1829:37 | StoreValue | r1829_6 | +| ir.cpp:1829:23:1829:37 | Unary | r1829_4 | +| ir.cpp:1829:23:1829:37 | Unary | r1829_5 | | perf-regression.cpp:6:3:6:5 | Address | &:r6_5 | | perf-regression.cpp:6:3:6:5 | Address | &:r6_5 | | perf-regression.cpp:6:3:6:5 | Address | &:r6_7 | @@ -8700,6 +8747,34 @@ | smart_ptr.cpp:47:43:47:63 | SideEffect | ~m47_16 | | smart_ptr.cpp:47:43:47:63 | Unary | r47_5 | | smart_ptr.cpp:47:43:47:63 | Unary | r47_6 | +| struct_init.cpp:9:13:9:25 | Left | r9_3 | +| struct_init.cpp:9:13:9:25 | Left | r9_3 | +| struct_init.cpp:9:13:9:25 | SideEffect | ~m11_10 | +| struct_init.cpp:9:31:12:1 | Right | r9_4 | +| struct_init.cpp:9:31:12:1 | Right | r9_6 | +| struct_init.cpp:9:31:12:1 | Unary | r9_5 | +| struct_init.cpp:9:31:12:1 | Unary | r9_5 | +| struct_init.cpp:9:31:12:1 | Unary | r9_7 | +| struct_init.cpp:9:31:12:1 | Unary | r9_7 | +| struct_init.cpp:10:5:10:21 | Address | &:r10_1 | +| struct_init.cpp:10:5:10:21 | Address | &:r10_6 | +| struct_init.cpp:10:7:10:9 | ChiPartial | partial:m10_4 | +| struct_init.cpp:10:7:10:9 | ChiTotal | total:m9_2 | +| struct_init.cpp:10:7:10:9 | StoreValue | r10_3 | +| struct_init.cpp:10:7:10:9 | Unary | r10_2 | +| struct_init.cpp:10:12:10:19 | ChiPartial | partial:m10_8 | +| struct_init.cpp:10:12:10:19 | ChiTotal | total:m10_5 | +| struct_init.cpp:10:12:10:19 | StoreValue | r10_7 | +| struct_init.cpp:11:5:11:22 | Address | &:r11_1 | +| struct_init.cpp:11:5:11:22 | Address | &:r11_6 | +| struct_init.cpp:11:7:11:9 | ChiPartial | partial:m11_4 | +| struct_init.cpp:11:7:11:9 | ChiTotal | total:m10_9 | +| struct_init.cpp:11:7:11:9 | StoreValue | r11_3 | +| struct_init.cpp:11:7:11:9 | Unary | r11_2 | +| struct_init.cpp:11:12:11:20 | ChiPartial | partial:m11_9 | +| struct_init.cpp:11:12:11:20 | ChiTotal | total:m11_5 | +| struct_init.cpp:11:12:11:20 | StoreValue | r11_8 | +| struct_init.cpp:11:13:11:20 | Unary | r11_7 | | struct_init.cpp:16:6:16:20 | ChiPartial | partial:m16_3 | | struct_init.cpp:16:6:16:20 | ChiTotal | total:m16_2 | | struct_init.cpp:16:6:16:20 | SideEffect | ~m17_5 | diff --git a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected index 17c59485eb9..849dad025bb 100644 --- a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected +++ b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected @@ -5650,6 +5650,19 @@ ir.cpp: # 1034| v1034_5(void) = AliasedUse : ~m? # 1034| v1034_6(void) = ExitFunction : +# 1038| (lambda [] type at line 1038, col. 12) lam +# 1038| Block 0 +# 1038| v1038_1(void) = EnterFunction : +# 1038| mu1038_2(unknown) = AliasedDefinition : +# 1038| r1038_3(glval) = VariableAddress : +# 1038| r1038_4(glval) = VariableAddress : +# 1038| mu1038_5(decltype([...](...){...})) = Uninitialized : &:r1038_4 +# 1038| r1038_6(decltype([...](...){...})) = Load[?] : &:r1038_4, ~m? +# 1038| mu1038_7(decltype([...](...){...})) = Store[?] : &:r1038_3, r1038_6 +# 1038| v1038_8(void) = ReturnVoid : +# 1038| v1038_9(void) = AliasedUse : ~m? +# 1038| v1038_10(void) = ExitFunction : + # 1038| void (lambda [] type at line 1038, col. 12)::operator()() const # 1038| Block 0 # 1038| v1038_1(void) = EnterFunction : @@ -9720,6 +9733,69 @@ ir.cpp: # 1785| v1785_7(void) = AliasedUse : ~m? # 1785| v1785_8(void) = ExitFunction : +# 1821| int global_2 +# 1821| Block 0 +# 1821| v1821_1(void) = EnterFunction : +# 1821| mu1821_2(unknown) = AliasedDefinition : +# 1821| r1821_3(glval) = VariableAddress : +# 1821| r1821_4(int) = Constant[1] : +# 1821| mu1821_5(int) = Store[?] : &:r1821_3, r1821_4 +# 1821| v1821_6(void) = ReturnVoid : +# 1821| v1821_7(void) = AliasedUse : ~m? +# 1821| v1821_8(void) = ExitFunction : + +# 1823| int const global_3 +# 1823| Block 0 +# 1823| v1823_1(void) = EnterFunction : +# 1823| mu1823_2(unknown) = AliasedDefinition : +# 1823| r1823_3(glval) = VariableAddress : +# 1823| r1823_4(int) = Constant[2] : +# 1823| mu1823_5(int) = Store[?] : &:r1823_3, r1823_4 +# 1823| v1823_6(void) = ReturnVoid : +# 1823| v1823_7(void) = AliasedUse : ~m? +# 1823| v1823_8(void) = ExitFunction : + +# 1825| constructor_only global_4 +# 1825| Block 0 +# 1825| v1825_1(void) = EnterFunction : +# 1825| mu1825_2(unknown) = AliasedDefinition : +# 1825| r1825_3(glval) = VariableAddress : +# 1825| r1825_4(glval) = FunctionAddress[constructor_only] : +# 1825| r1825_5(int) = Constant[1] : +# 1825| v1825_6(void) = Call[constructor_only] : func:r1825_4, this:r1825_3, 0:r1825_5 +# 1825| mu1825_7(unknown) = ^CallSideEffect : ~m? +# 1825| mu1825_8(constructor_only) = ^IndirectMayWriteSideEffect[-1] : &:r1825_3 +# 1825| v1825_9(void) = ReturnVoid : +# 1825| v1825_10(void) = AliasedUse : ~m? +# 1825| v1825_11(void) = ExitFunction : + +# 1827| constructor_only global_5 +# 1827| Block 0 +# 1827| v1827_1(void) = EnterFunction : +# 1827| mu1827_2(unknown) = AliasedDefinition : +# 1827| r1827_3(glval) = VariableAddress : +# 1827| r1827_4(glval) = FunctionAddress[constructor_only] : +# 1827| r1827_5(int) = Constant[2] : +# 1827| v1827_6(void) = Call[constructor_only] : func:r1827_4, this:r1827_3, 0:r1827_5 +# 1827| mu1827_7(unknown) = ^CallSideEffect : ~m? +# 1827| mu1827_8(constructor_only) = ^IndirectMayWriteSideEffect[-1] : &:r1827_3 +# 1827| v1827_9(void) = ReturnVoid : +# 1827| v1827_10(void) = AliasedUse : ~m? +# 1827| v1827_11(void) = ExitFunction : + +# 1829| char* global_string +# 1829| Block 0 +# 1829| v1829_1(void) = EnterFunction : +# 1829| mu1829_2(unknown) = AliasedDefinition : +# 1829| r1829_3(glval) = VariableAddress : +# 1829| r1829_4(glval) = StringConstant : +# 1829| r1829_5(char *) = Convert : r1829_4 +# 1829| r1829_6(char *) = Convert : r1829_5 +# 1829| mu1829_7(char *) = Store[?] : &:r1829_3, r1829_6 +# 1829| v1829_8(void) = ReturnVoid : +# 1829| v1829_9(void) = AliasedUse : ~m? +# 1829| v1829_10(void) = ExitFunction : + perf-regression.cpp: # 6| void Big::Big() # 6| Block 0 @@ -9941,6 +10017,34 @@ smart_ptr.cpp: # 28| v28_6(void) = ExitFunction : struct_init.cpp: +# 9| Info infos_in_file[] +# 9| Block 0 +# 9| v9_1(void) = EnterFunction : +# 9| mu9_2(unknown) = AliasedDefinition : +# 9| r9_3(glval) = VariableAddress : +# 9| r9_4(int) = Constant[0] : +# 9| r9_5(glval) = PointerAdd[16] : r9_3, r9_4 +# 10| r10_1(glval) = FieldAddress[name] : r9_5 +# 10| r10_2(glval) = StringConstant : +# 10| r10_3(char *) = Convert : r10_2 +# 10| mu10_4(char *) = Store[?] : &:r10_1, r10_3 +# 10| r10_5(glval<..(*)(..)>) = FieldAddress[handler] : r9_5 +# 10| r10_6(..(*)(..)) = FunctionAddress[handler1] : +# 10| mu10_7(..(*)(..)) = Store[?] : &:r10_5, r10_6 +# 9| r9_6(int) = Constant[1] : +# 9| r9_7(glval) = PointerAdd[16] : r9_3, r9_6 +# 11| r11_1(glval) = FieldAddress[name] : r9_7 +# 11| r11_2(glval) = StringConstant : +# 11| r11_3(char *) = Convert : r11_2 +# 11| mu11_4(char *) = Store[?] : &:r11_1, r11_3 +# 11| r11_5(glval<..(*)(..)>) = FieldAddress[handler] : r9_7 +# 11| r11_6(glval<..()(..)>) = FunctionAddress[handler2] : +# 11| r11_7(..(*)(..)) = CopyValue : r11_6 +# 11| mu11_8(..(*)(..)) = Store[?] : &:r11_5, r11_7 +# 9| v9_8(void) = ReturnVoid : +# 9| v9_9(void) = AliasedUse : ~m? +# 9| v9_10(void) = ExitFunction : + # 16| void let_info_escape(Info*) # 16| Block 0 # 16| v16_1(void) = EnterFunction : diff --git a/cpp/ql/test/library-tests/ir/ir/raw_ir.ql b/cpp/ql/test/library-tests/ir/ir/raw_ir.ql index a0ebe4d2bdd..ae37a4a932b 100644 --- a/cpp/ql/test/library-tests/ir/ir/raw_ir.ql +++ b/cpp/ql/test/library-tests/ir/ir/raw_ir.ql @@ -7,5 +7,5 @@ private import semmle.code.cpp.ir.implementation.raw.PrintIR private import PrintConfig private class PrintConfig extends PrintIRConfiguration { - override predicate shouldPrintFunction(Function func) { shouldDumpFunction(func) } + override predicate shouldPrintFunction(Declaration decl) { shouldDumpFunction(decl) } } diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected index 147c10b7c7f..1893ab5c0d5 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected @@ -1234,6 +1234,8 @@ ssa.cpp: # 268| v268_14(void) = AliasedUse : ~m269_7 # 268| v268_15(void) = ExitFunction : +# 274| Point* pp + # 275| void EscapedButNotConflated(bool, Point, int) # 275| Block 0 # 275| v275_1(void) = EnterFunction : diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected index 396b7532d68..faedd418ed2 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected @@ -1229,6 +1229,8 @@ ssa.cpp: # 268| v268_14(void) = AliasedUse : ~m269_7 # 268| v268_15(void) = ExitFunction : +# 274| Point* pp + # 275| void EscapedButNotConflated(bool, Point, int) # 275| Block 0 # 275| v275_1(void) = EnterFunction : diff --git a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected index 3fc07bf6950..6d1e8f4d03d 100644 --- a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected +++ b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected @@ -1140,6 +1140,8 @@ ssa.cpp: # 268| v268_13(void) = AliasedUse : ~m? # 268| v268_14(void) = ExitFunction : +# 274| Point* pp + # 275| void EscapedButNotConflated(bool, Point, int) # 275| Block 0 # 275| v275_1(void) = EnterFunction : diff --git a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected index 3fc07bf6950..6d1e8f4d03d 100644 --- a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected @@ -1140,6 +1140,8 @@ ssa.cpp: # 268| v268_13(void) = AliasedUse : ~m? # 268| v268_14(void) = ExitFunction : +# 274| Point* pp + # 275| void EscapedButNotConflated(bool, Point, int) # 275| Block 0 # 275| v275_1(void) = EnterFunction : diff --git a/cpp/ql/test/library-tests/syntax-zoo/aliased_ssa_consistency.expected b/cpp/ql/test/library-tests/syntax-zoo/aliased_ssa_consistency.expected index fcfef712b56..db803126364 100644 --- a/cpp/ql/test/library-tests/syntax-zoo/aliased_ssa_consistency.expected +++ b/cpp/ql/test/library-tests/syntax-zoo/aliased_ssa_consistency.expected @@ -4,6 +4,8 @@ unexpectedOperand duplicateOperand missingPhiOperand missingOperandType +| cpp11.cpp:36:18:36:18 | ChiTotal | Operand 'ChiTotal' of instruction 'Chi' is missing a type in function '$@'. | cpp11.cpp:36:5:36:14 | int global_int | int global_int | +| misc.c:210:24:210:28 | ChiTotal | Operand 'ChiTotal' of instruction 'Chi' is missing a type in function '$@'. | misc.c:210:5:210:20 | int global_with_init | int global_with_init | duplicateChiOperand sideEffectWithoutPrimary instructionWithoutSuccessor @@ -91,6 +93,8 @@ useNotDominatedByDefinition switchInstructionWithoutDefaultEdge notMarkedAsConflated wronglyMarkedAsConflated +| cpp11.cpp:36:18:36:18 | Chi: 5 | Instruction 'Chi: 5' should not be marked as having a conflated result in function '$@'. | cpp11.cpp:36:5:36:14 | int global_int | int global_int | +| misc.c:210:24:210:28 | Chi: ... + ... | Instruction 'Chi: ... + ...' should not be marked as having a conflated result in function '$@'. | misc.c:210:5:210:20 | int global_with_init | int global_with_init | invalidOverlap nonUniqueEnclosingIRFunction fieldAddressOnNonPointer diff --git a/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected index e37a676565c..53bdffc3be3 100644 --- a/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected +++ b/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected @@ -1,4 +1,45 @@ uniqueEnclosingCallable +| cpp11.cpp:35:11:35:22 | Address | Node should have one enclosing callable but has 0. | +| cpp11.cpp:35:11:35:22 | AliasedDefinition | Node should have one enclosing callable but has 0. | +| cpp11.cpp:35:11:35:22 | VariableAddress | Node should have one enclosing callable but has 0. | +| cpp11.cpp:35:11:35:22 | VariableAddress [post update] | Node should have one enclosing callable but has 0. | +| cpp11.cpp:35:26:35:26 | 5 | Node should have one enclosing callable but has 0. | +| cpp11.cpp:35:26:35:26 | ChiPartial | Node should have one enclosing callable but has 0. | +| cpp11.cpp:35:26:35:26 | ChiTotal | Node should have one enclosing callable but has 0. | +| cpp11.cpp:35:26:35:26 | Store | Node should have one enclosing callable but has 0. | +| cpp11.cpp:35:26:35:26 | StoreValue | Node should have one enclosing callable but has 0. | +| cpp11.cpp:36:5:36:14 | Address | Node should have one enclosing callable but has 0. | +| cpp11.cpp:36:5:36:14 | VariableAddress | Node should have one enclosing callable but has 0. | +| cpp11.cpp:36:5:36:14 | VariableAddress [post update] | Node should have one enclosing callable but has 0. | +| cpp11.cpp:36:18:36:18 | 5 | Node should have one enclosing callable but has 0. | +| cpp11.cpp:36:18:36:18 | ChiPartial | Node should have one enclosing callable but has 0. | +| cpp11.cpp:36:18:36:18 | Store | Node should have one enclosing callable but has 0. | +| cpp11.cpp:36:18:36:18 | StoreValue | Node should have one enclosing callable but has 0. | +| misc.c:10:5:10:13 | Address | Node should have one enclosing callable but has 0. | +| misc.c:10:5:10:13 | AliasedDefinition | Node should have one enclosing callable but has 0. | +| misc.c:10:5:10:13 | VariableAddress | Node should have one enclosing callable but has 0. | +| misc.c:10:5:10:13 | VariableAddress [post update] | Node should have one enclosing callable but has 0. | +| misc.c:10:17:10:17 | 1 | Node should have one enclosing callable but has 0. | +| misc.c:10:17:10:17 | ChiPartial | Node should have one enclosing callable but has 0. | +| misc.c:10:17:10:17 | ChiTotal | Node should have one enclosing callable but has 0. | +| misc.c:10:17:10:17 | Store | Node should have one enclosing callable but has 0. | +| misc.c:10:17:10:17 | StoreValue | Node should have one enclosing callable but has 0. | +| misc.c:11:5:11:13 | Address | Node should have one enclosing callable but has 0. | +| misc.c:11:5:11:13 | AliasedDefinition | Node should have one enclosing callable but has 0. | +| misc.c:11:5:11:13 | VariableAddress | Node should have one enclosing callable but has 0. | +| misc.c:11:5:11:13 | VariableAddress [post update] | Node should have one enclosing callable but has 0. | +| misc.c:11:17:11:21 | ... + ... | Node should have one enclosing callable but has 0. | +| misc.c:11:17:11:21 | ChiPartial | Node should have one enclosing callable but has 0. | +| misc.c:11:17:11:21 | ChiTotal | Node should have one enclosing callable but has 0. | +| misc.c:11:17:11:21 | Store | Node should have one enclosing callable but has 0. | +| misc.c:11:17:11:21 | StoreValue | Node should have one enclosing callable but has 0. | +| misc.c:210:5:210:20 | Address | Node should have one enclosing callable but has 0. | +| misc.c:210:5:210:20 | VariableAddress | Node should have one enclosing callable but has 0. | +| misc.c:210:5:210:20 | VariableAddress [post update] | Node should have one enclosing callable but has 0. | +| misc.c:210:24:210:28 | ... + ... | Node should have one enclosing callable but has 0. | +| misc.c:210:24:210:28 | ChiPartial | Node should have one enclosing callable but has 0. | +| misc.c:210:24:210:28 | Store | Node should have one enclosing callable but has 0. | +| misc.c:210:24:210:28 | StoreValue | Node should have one enclosing callable but has 0. | uniqueType uniqueNodeLocation | aggregateinitializer.c:1:6:1:6 | AliasedDefinition | Node should have one location but has 20. | @@ -1622,6 +1663,8 @@ postWithInFlow | cpp11.cpp:28:21:28:34 | temporary object [post update] | PostUpdateNode should not be the target of local flow. | | cpp11.cpp:29:7:29:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. | | cpp11.cpp:31:5:31:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. | +| cpp11.cpp:35:11:35:22 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. | +| cpp11.cpp:36:5:36:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. | | cpp11.cpp:56:14:56:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. | | cpp11.cpp:56:14:56:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. | | cpp11.cpp:60:15:60:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. | @@ -2230,6 +2273,8 @@ postWithInFlow | ltrbinopexpr.c:37:5:37:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. | | ltrbinopexpr.c:39:5:39:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. | | ltrbinopexpr.c:40:5:40:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. | +| misc.c:10:5:10:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. | +| misc.c:11:5:11:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. | | misc.c:18:5:18:5 | i [post update] | PostUpdateNode should not be the target of local flow. | | misc.c:19:5:19:5 | i [post update] | PostUpdateNode should not be the target of local flow. | | misc.c:20:7:20:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. | @@ -2289,6 +2334,7 @@ postWithInFlow | misc.c:200:24:200:27 | args [post update] | PostUpdateNode should not be the target of local flow. | | misc.c:200:24:200:27 | array to pointer conversion [post update] | PostUpdateNode should not be the target of local flow. | | misc.c:208:1:208:3 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. | +| misc.c:210:5:210:20 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. | | misc.c:216:3:216:26 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. | | misc.c:220:3:220:5 | * ... [post update] | PostUpdateNode should not be the target of local flow. | | misc.c:220:4:220:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. | diff --git a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/diff_ir_expr.expected b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/diff_ir_expr.expected index 7322e9723ec..b838a13d5af 100644 --- a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/diff_ir_expr.expected +++ b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/diff_ir_expr.expected @@ -1,18 +1,15 @@ | test.cpp:5:3:5:13 | ... = ... | test.cpp:5:3:5:13 | ... = ... | AST only | | test.cpp:6:3:6:13 | ... = ... | test.cpp:6:3:6:13 | ... = ... | AST only | | test.cpp:7:3:7:7 | ... = ... | test.cpp:7:3:7:7 | ... = ... | AST only | -| test.cpp:10:16:10:16 | 1 | test.cpp:10:16:10:16 | 1 | AST only | | test.cpp:16:3:16:24 | ... = ... | test.cpp:16:3:16:24 | ... = ... | AST only | | test.cpp:17:3:17:24 | ... = ... | test.cpp:17:3:17:24 | ... = ... | AST only | | test.cpp:18:3:18:7 | ... = ... | test.cpp:18:3:18:7 | ... = ... | AST only | -| test.cpp:21:16:21:16 | 2 | test.cpp:21:16:21:16 | 2 | AST only | | test.cpp:29:3:29:3 | x | test.cpp:31:3:31:3 | x | IR only | | test.cpp:29:3:29:24 | ... = ... | test.cpp:29:3:29:24 | ... = ... | AST only | | test.cpp:30:3:30:17 | call to change_global02 | test.cpp:30:3:30:17 | call to change_global02 | AST only | | test.cpp:31:3:31:3 | x | test.cpp:29:3:29:3 | x | IR only | | test.cpp:31:3:31:24 | ... = ... | test.cpp:31:3:31:24 | ... = ... | AST only | | test.cpp:32:3:32:7 | ... = ... | test.cpp:32:3:32:7 | ... = ... | AST only | -| test.cpp:35:16:35:16 | 3 | test.cpp:35:16:35:16 | 3 | AST only | | test.cpp:43:3:43:3 | x | test.cpp:45:3:45:3 | x | IR only | | test.cpp:43:3:43:24 | ... = ... | test.cpp:43:3:43:24 | ... = ... | AST only | | test.cpp:43:7:43:24 | ... + ... | test.cpp:45:7:45:24 | ... + ... | IR only | diff --git a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected index 24dc1c1ab44..94941f4a70b 100644 --- a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected +++ b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected @@ -69,6 +69,23 @@ test.cpp: # 1| v1_10(void) = AliasedUse : m1_3 # 1| v1_11(void) = ExitFunction : +# 10| int global01 +# 10| Block 0 +# 10| v10_1(void) = EnterFunction : +# 10| m10_2(unknown) = AliasedDefinition : +# 10| valnum = unique +# 10| r10_3(glval) = VariableAddress[global01] : +# 10| valnum = unique +# 10| r10_4(int) = Constant[1] : +# 10| valnum = m10_5, r10_4 +# 10| m10_5(int) = Store[global01] : &:r10_3, r10_4 +# 10| valnum = m10_5, r10_4 +# 10| m10_6(unknown) = Chi : total:~m?, partial:m10_5 +# 10| valnum = unique +# 10| v10_7(void) = ReturnVoid : +# 10| v10_8(void) = AliasedUse : ~m10_2 +# 10| v10_9(void) = ExitFunction : + # 12| void test01(int, int) # 12| Block 0 # 12| v12_1(void) = EnterFunction : @@ -151,6 +168,23 @@ test.cpp: # 12| v12_10(void) = AliasedUse : m12_3 # 12| v12_11(void) = ExitFunction : +# 21| int global02 +# 21| Block 0 +# 21| v21_1(void) = EnterFunction : +# 21| m21_2(unknown) = AliasedDefinition : +# 21| valnum = unique +# 21| r21_3(glval) = VariableAddress[global02] : +# 21| valnum = unique +# 21| r21_4(int) = Constant[2] : +# 21| valnum = m21_5, r21_4 +# 21| m21_5(int) = Store[global02] : &:r21_3, r21_4 +# 21| valnum = m21_5, r21_4 +# 21| m21_6(unknown) = Chi : total:~m?, partial:m21_5 +# 21| valnum = unique +# 21| v21_7(void) = ReturnVoid : +# 21| v21_8(void) = AliasedUse : ~m21_2 +# 21| v21_9(void) = ExitFunction : + # 25| void test02(int, int) # 25| Block 0 # 25| v25_1(void) = EnterFunction : @@ -240,6 +274,23 @@ test.cpp: # 25| v25_10(void) = AliasedUse : ~m30_4 # 25| v25_11(void) = ExitFunction : +# 35| int global03 +# 35| Block 0 +# 35| v35_1(void) = EnterFunction : +# 35| m35_2(unknown) = AliasedDefinition : +# 35| valnum = unique +# 35| r35_3(glval) = VariableAddress[global03] : +# 35| valnum = unique +# 35| r35_4(int) = Constant[3] : +# 35| valnum = m35_5, r35_4 +# 35| m35_5(int) = Store[global03] : &:r35_3, r35_4 +# 35| valnum = m35_5, r35_4 +# 35| m35_6(unknown) = Chi : total:~m?, partial:m35_5 +# 35| valnum = unique +# 35| v35_7(void) = ReturnVoid : +# 35| v35_8(void) = AliasedUse : ~m35_2 +# 35| v35_9(void) = ExitFunction : + # 39| void test03(int, int, int*) # 39| Block 0 # 39| v39_1(void) = EnterFunction : @@ -890,6 +941,10 @@ test.cpp: # 124| v124_13(void) = AliasedUse : m124_3 # 124| v124_14(void) = ExitFunction : +# 132| A* global_a + +# 133| int global_n + # 135| void test_read_global_same() # 135| Block 0 # 135| v135_1(void) = EnterFunction : diff --git a/csharp/ql/src/experimental/ir/implementation/IRConfiguration.qll b/csharp/ql/src/experimental/ir/implementation/IRConfiguration.qll index 37ac2fccdd9..90cdb9e0f5f 100644 --- a/csharp/ql/src/experimental/ir/implementation/IRConfiguration.qll +++ b/csharp/ql/src/experimental/ir/implementation/IRConfiguration.qll @@ -16,7 +16,7 @@ class IRConfiguration extends TIRConfiguration { /** * Holds if IR should be created for function `func`. By default, holds for all functions. */ - predicate shouldCreateIRForFunction(Language::Function func) { any() } + predicate shouldCreateIRForFunction(Language::Declaration func) { any() } /** * Holds if the strings used as part of an IR dump should be generated for function `func`. @@ -25,7 +25,7 @@ class IRConfiguration extends TIRConfiguration { * of debug strings for IR that will not be dumped. We still generate the actual IR for these * functions, however, to preserve the results of any interprocedural analysis. */ - predicate shouldEvaluateDebugStringsForFunction(Language::Function func) { any() } + predicate shouldEvaluateDebugStringsForFunction(Language::Declaration func) { any() } } private newtype TIREscapeAnalysisConfiguration = MkIREscapeAnalysisConfiguration() diff --git a/csharp/ql/src/experimental/ir/implementation/internal/IRFunctionBase.qll b/csharp/ql/src/experimental/ir/implementation/internal/IRFunctionBase.qll index 60895ce3d26..576b4f9adf1 100644 --- a/csharp/ql/src/experimental/ir/implementation/internal/IRFunctionBase.qll +++ b/csharp/ql/src/experimental/ir/implementation/internal/IRFunctionBase.qll @@ -5,23 +5,28 @@ private import IRFunctionBaseInternal private newtype TIRFunction = - MkIRFunction(Language::Function func) { IRConstruction::Raw::functionHasIR(func) } + TFunctionIRFunction(Language::Function func) { IRConstruction::Raw::functionHasIR(func) } or + TVarInitIRFunction(Language::GlobalVariable var) { IRConstruction::Raw::varHasIRFunc(var) } /** * The IR for a function. This base class contains only the predicates that are the same between all * phases of the IR. Each instantiation of `IRFunction` extends this class. */ class IRFunctionBase extends TIRFunction { - Language::Function func; + Language::Declaration decl; - IRFunctionBase() { this = MkIRFunction(func) } + IRFunctionBase() { + this = TFunctionIRFunction(decl) + or + this = TVarInitIRFunction(decl) + } /** Gets a textual representation of this element. */ - final string toString() { result = "IR: " + func.toString() } + final string toString() { result = "IR: " + decl.toString() } /** Gets the function whose IR is represented. */ - final Language::Function getFunction() { result = func } + final Language::Declaration getFunction() { result = decl } /** Gets the location of the function. */ - final Language::Location getLocation() { result = func.getLocation() } + final Language::Location getLocation() { result = decl.getLocation() } } diff --git a/csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll b/csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll index bac7634cbd0..78008a6c69b 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll @@ -97,7 +97,7 @@ class IRBlockBase extends TIRBlock { /** * Gets the `Function` that contains this block. */ - final Language::Function getEnclosingFunction() { + final Language::Declaration getEnclosingFunction() { result = getFirstInstruction(this).getEnclosingFunction() } } diff --git a/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll b/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll index e5a908bbf9a..8e863ddf635 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll @@ -194,7 +194,7 @@ class Instruction extends Construction::TStageInstruction { /** * Gets the function that contains this instruction. */ - final Language::Function getEnclosingFunction() { + final Language::Declaration getEnclosingFunction() { result = this.getEnclosingIRFunction().getFunction() } diff --git a/csharp/ql/src/experimental/ir/implementation/raw/PrintIR.qll b/csharp/ql/src/experimental/ir/implementation/raw/PrintIR.qll index 59dadee7154..53cdc75512b 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/PrintIR.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/PrintIR.qll @@ -26,20 +26,20 @@ class PrintIRConfiguration extends TPrintIRConfiguration { * Holds if the IR for `func` should be printed. By default, holds for all * functions. */ - predicate shouldPrintFunction(Language::Function func) { any() } + predicate shouldPrintFunction(Language::Declaration decl) { any() } } /** * Override of `IRConfiguration` to only evaluate debug strings for the functions that are to be dumped. */ private class FilteredIRConfiguration extends IRConfiguration { - override predicate shouldEvaluateDebugStringsForFunction(Language::Function func) { + override predicate shouldEvaluateDebugStringsForFunction(Language::Declaration func) { shouldPrintFunction(func) } } -private predicate shouldPrintFunction(Language::Function func) { - exists(PrintIRConfiguration config | config.shouldPrintFunction(func)) +private predicate shouldPrintFunction(Language::Declaration decl) { + exists(PrintIRConfiguration config | config.shouldPrintFunction(decl)) } private string getAdditionalInstructionProperty(Instruction instr, string key) { diff --git a/csharp/ql/src/experimental/ir/implementation/raw/internal/IRConstruction.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/IRConstruction.qll index db6bd5c24e5..80002ffc020 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/internal/IRConstruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/IRConstruction.qll @@ -46,6 +46,9 @@ module Raw { cached predicate functionHasIR(Callable callable) { exists(getTranslatedFunction(callable)) } + cached + predicate varHasIRFunc(Field field) { none() } + cached predicate hasInstruction(TranslatedElement element, InstructionTag tag) { element.hasInstruction(_, tag, _) diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll index bac7634cbd0..78008a6c69b 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll @@ -97,7 +97,7 @@ class IRBlockBase extends TIRBlock { /** * Gets the `Function` that contains this block. */ - final Language::Function getEnclosingFunction() { + final Language::Declaration getEnclosingFunction() { result = getFirstInstruction(this).getEnclosingFunction() } } diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll index e5a908bbf9a..8e863ddf635 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll @@ -194,7 +194,7 @@ class Instruction extends Construction::TStageInstruction { /** * Gets the function that contains this instruction. */ - final Language::Function getEnclosingFunction() { + final Language::Declaration getEnclosingFunction() { result = this.getEnclosingIRFunction().getFunction() } diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.qll index 59dadee7154..53cdc75512b 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.qll @@ -26,20 +26,20 @@ class PrintIRConfiguration extends TPrintIRConfiguration { * Holds if the IR for `func` should be printed. By default, holds for all * functions. */ - predicate shouldPrintFunction(Language::Function func) { any() } + predicate shouldPrintFunction(Language::Declaration decl) { any() } } /** * Override of `IRConfiguration` to only evaluate debug strings for the functions that are to be dumped. */ private class FilteredIRConfiguration extends IRConfiguration { - override predicate shouldEvaluateDebugStringsForFunction(Language::Function func) { + override predicate shouldEvaluateDebugStringsForFunction(Language::Declaration func) { shouldPrintFunction(func) } } -private predicate shouldPrintFunction(Language::Function func) { - exists(PrintIRConfiguration config | config.shouldPrintFunction(func)) +private predicate shouldPrintFunction(Language::Declaration decl) { + exists(PrintIRConfiguration config | config.shouldPrintFunction(decl)) } private string getAdditionalInstructionProperty(Instruction instr, string key) { diff --git a/csharp/ql/src/experimental/ir/internal/IRCSharpLanguage.qll b/csharp/ql/src/experimental/ir/internal/IRCSharpLanguage.qll index 88c27315c2f..11fbe784ca0 100644 --- a/csharp/ql/src/experimental/ir/internal/IRCSharpLanguage.qll +++ b/csharp/ql/src/experimental/ir/internal/IRCSharpLanguage.qll @@ -8,6 +8,12 @@ class OpaqueTypeTag = CSharp::ValueOrRefType; class Function = CSharp::Callable; +class GlobalVariable extends CSharp::Field { + GlobalVariable() { this.isStatic() } +} + +class Declaration = CSharp::Declaration; + class Location = CSharp::Location; class UnknownLocation = CSharp::EmptyLocation; From e0878d7d3c95695b3b281831ce4424855ef282a7 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Wed, 27 Apr 2022 16:35:58 -0400 Subject: [PATCH 058/505] C++: Fix IR variable reuse for global var inits --- .../implementation/aliased_ssa/IRVariable.qll | 6 +-- .../implementation/internal/TIRVariable.qll | 8 +-- .../cpp/ir/implementation/raw/IRVariable.qll | 6 +-- .../raw/internal/TranslatedGlobalVar.qll | 3 +- .../unaliased_ssa/IRVariable.qll | 6 +-- .../dataflow-ir-consistency.expected | 6 ++- .../ir/ir/operand_locations.expected | 10 ++-- .../test/library-tests/ir/ir/raw_ir.expected | 44 +++++++-------- .../GlobalValueNumbering/ir_gvn.expected | 54 +++++++++---------- .../implementation/internal/TIRVariable.qll | 8 +-- .../ir/implementation/raw/IRVariable.qll | 6 +-- .../unaliased_ssa/IRVariable.qll | 6 +-- 12 files changed, 83 insertions(+), 80 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll index ca4708857a7..c92082d767d 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll @@ -18,7 +18,7 @@ private import Imports::IRType * by the AST-to-IR translation (`IRTempVariable`). */ class IRVariable extends TIRVariable { - Language::Function func; + Language::Declaration func; IRVariable() { this = TIRUserVariable(_, _, func) or @@ -79,7 +79,7 @@ class IRVariable extends TIRVariable { /** * Gets the function that references this variable. */ - final Language::Function getEnclosingFunction() { result = func } + final Language::Declaration getEnclosingFunction() { result = func } } /** @@ -246,7 +246,7 @@ class IREllipsisVariable extends IRTempVariable, IRParameter { final override string toString() { result = "#ellipsis" } - final override int getIndex() { result = func.getNumberOfParameters() } + final override int getIndex() { result = func.(Language::Function).getNumberOfParameters() } } /** diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/internal/TIRVariable.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/internal/TIRVariable.qll index 12a0c6e7898..fe72263249f 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/internal/TIRVariable.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/internal/TIRVariable.qll @@ -2,21 +2,21 @@ private import TIRVariableInternal private import Imports::TempVariableTag newtype TIRVariable = - TIRUserVariable(Language::Variable var, Language::LanguageType type, Language::Function func) { + TIRUserVariable(Language::Variable var, Language::LanguageType type, Language::Declaration func) { Construction::hasUserVariable(func, var, type) } or TIRTempVariable( - Language::Function func, Language::AST ast, TempVariableTag tag, Language::LanguageType type + Language::Declaration func, Language::AST ast, TempVariableTag tag, Language::LanguageType type ) { Construction::hasTempVariable(func, ast, tag, type) } or TIRDynamicInitializationFlag( - Language::Function func, Language::Variable var, Language::LanguageType type + Language::Declaration func, Language::Variable var, Language::LanguageType type ) { Construction::hasDynamicInitializationFlag(func, var, type) } or TIRStringLiteral( - Language::Function func, Language::AST ast, Language::LanguageType type, + Language::Declaration func, Language::AST ast, Language::LanguageType type, Language::StringLiteral literal ) { Construction::hasStringLiteral(func, ast, type, literal) diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRVariable.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRVariable.qll index ca4708857a7..c92082d767d 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRVariable.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRVariable.qll @@ -18,7 +18,7 @@ private import Imports::IRType * by the AST-to-IR translation (`IRTempVariable`). */ class IRVariable extends TIRVariable { - Language::Function func; + Language::Declaration func; IRVariable() { this = TIRUserVariable(_, _, func) or @@ -79,7 +79,7 @@ class IRVariable extends TIRVariable { /** * Gets the function that references this variable. */ - final Language::Function getEnclosingFunction() { result = func } + final Language::Declaration getEnclosingFunction() { result = func } } /** @@ -246,7 +246,7 @@ class IREllipsisVariable extends IRTempVariable, IRParameter { final override string toString() { result = "#ellipsis" } - final override int getIndex() { result = func.getNumberOfParameters() } + final override int getIndex() { result = func.(Language::Function).getNumberOfParameters() } } /** diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedGlobalVar.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedGlobalVar.qll index abc175b7040..bef32ede64a 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedGlobalVar.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedGlobalVar.qll @@ -89,7 +89,8 @@ class TranslatedGlobalOrNamespaceVarInit extends TranslatedRootElement, override IRUserVariable getInstructionVariable(InstructionTag tag) { tag = InitializerVariableAddressTag() and - result.getVariable() = var + result.getVariable() = var and + result.getEnclosingFunction() = var } override Instruction getTargetAddress() { diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll index ca4708857a7..c92082d767d 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll @@ -18,7 +18,7 @@ private import Imports::IRType * by the AST-to-IR translation (`IRTempVariable`). */ class IRVariable extends TIRVariable { - Language::Function func; + Language::Declaration func; IRVariable() { this = TIRUserVariable(_, _, func) or @@ -79,7 +79,7 @@ class IRVariable extends TIRVariable { /** * Gets the function that references this variable. */ - final Language::Function getEnclosingFunction() { result = func } + final Language::Declaration getEnclosingFunction() { result = func } } /** @@ -246,7 +246,7 @@ class IREllipsisVariable extends IRTempVariable, IRParameter { final override string toString() { result = "#ellipsis" } - final override int getIndex() { result = func.getNumberOfParameters() } + final override int getIndex() { result = func.(Language::Function).getNumberOfParameters() } } /** diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected index 056d4b6bfe3..f4a3d208614 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected @@ -1,16 +1,20 @@ uniqueEnclosingCallable | globals.cpp:9:5:9:19 | Address | Node should have one enclosing callable but has 0. | +| globals.cpp:9:5:9:19 | AliasedDefinition | Node should have one enclosing callable but has 0. | | globals.cpp:9:5:9:19 | VariableAddress | Node should have one enclosing callable but has 0. | | globals.cpp:9:5:9:19 | VariableAddress [post update] | Node should have one enclosing callable but has 0. | | globals.cpp:9:23:9:23 | 0 | Node should have one enclosing callable but has 0. | | globals.cpp:9:23:9:23 | ChiPartial | Node should have one enclosing callable but has 0. | +| globals.cpp:9:23:9:23 | ChiTotal | Node should have one enclosing callable but has 0. | | globals.cpp:9:23:9:23 | Store | Node should have one enclosing callable but has 0. | | globals.cpp:9:23:9:23 | StoreValue | Node should have one enclosing callable but has 0. | | globals.cpp:16:12:16:26 | Address | Node should have one enclosing callable but has 0. | +| globals.cpp:16:12:16:26 | AliasedDefinition | Node should have one enclosing callable but has 0. | | globals.cpp:16:12:16:26 | VariableAddress | Node should have one enclosing callable but has 0. | | globals.cpp:16:12:16:26 | VariableAddress [post update] | Node should have one enclosing callable but has 0. | | globals.cpp:16:30:16:30 | 0 | Node should have one enclosing callable but has 0. | | globals.cpp:16:30:16:30 | ChiPartial | Node should have one enclosing callable but has 0. | +| globals.cpp:16:30:16:30 | ChiTotal | Node should have one enclosing callable but has 0. | | globals.cpp:16:30:16:30 | Store | Node should have one enclosing callable but has 0. | | globals.cpp:16:30:16:30 | StoreValue | Node should have one enclosing callable but has 0. | uniqueType @@ -234,10 +238,10 @@ postWithInFlow | lambdas.cpp:20:11:20:11 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. | | lambdas.cpp:20:11:20:11 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. | | lambdas.cpp:20:11:20:11 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. | +| lambdas.cpp:23:3:23:3 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. | | lambdas.cpp:23:3:23:14 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. | | lambdas.cpp:23:3:23:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. | | lambdas.cpp:23:3:23:14 | v [post update] | PostUpdateNode should not be the target of local flow. | -| lambdas.cpp:23:15:23:15 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. | | lambdas.cpp:28:7:28:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. | | lambdas.cpp:28:10:31:2 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. | | lambdas.cpp:28:10:31:2 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. | diff --git a/cpp/ql/test/library-tests/ir/ir/operand_locations.expected b/cpp/ql/test/library-tests/ir/ir/operand_locations.expected index e0e04301e6f..58f3b25feb2 100644 --- a/cpp/ql/test/library-tests/ir/ir/operand_locations.expected +++ b/cpp/ql/test/library-tests/ir/ir/operand_locations.expected @@ -4744,15 +4744,13 @@ | ir.cpp:1034:6:1034:20 | SideEffect | m1034_3 | | ir.cpp:1035:15:1035:15 | Address | &:r1035_1 | | ir.cpp:1038:6:1038:8 | Address | &:r1038_3 | -| ir.cpp:1038:6:1038:8 | SideEffect | ~m1038_9 | +| ir.cpp:1038:6:1038:8 | SideEffect | ~m1038_8 | | ir.cpp:1038:12:1038:18 | Address | &:r1038_4 | | ir.cpp:1038:12:1038:18 | Address | &:r1038_4 | -| ir.cpp:1038:12:1038:18 | ChiPartial | partial:m1038_5 | -| ir.cpp:1038:12:1038:18 | ChiPartial | partial:m1038_8 | +| ir.cpp:1038:12:1038:18 | ChiPartial | partial:m1038_7 | | ir.cpp:1038:12:1038:18 | ChiTotal | total:m1038_2 | -| ir.cpp:1038:12:1038:18 | ChiTotal | total:m1038_6 | -| ir.cpp:1038:12:1038:18 | Load | ~m1038_6 | -| ir.cpp:1038:12:1038:18 | StoreValue | r1038_7 | +| ir.cpp:1038:12:1038:18 | Load | m1038_5 | +| ir.cpp:1038:12:1038:18 | StoreValue | r1038_6 | | ir.cpp:1038:14:1038:14 | Address | &:r1038_5 | | ir.cpp:1038:14:1038:14 | Address | &:r1038_5 | | ir.cpp:1038:14:1038:14 | Address | &:r1038_5 | diff --git a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected index 849dad025bb..397f2bb288c 100644 --- a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected +++ b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected @@ -5652,16 +5652,16 @@ ir.cpp: # 1038| (lambda [] type at line 1038, col. 12) lam # 1038| Block 0 -# 1038| v1038_1(void) = EnterFunction : -# 1038| mu1038_2(unknown) = AliasedDefinition : -# 1038| r1038_3(glval) = VariableAddress : -# 1038| r1038_4(glval) = VariableAddress : -# 1038| mu1038_5(decltype([...](...){...})) = Uninitialized : &:r1038_4 -# 1038| r1038_6(decltype([...](...){...})) = Load[?] : &:r1038_4, ~m? -# 1038| mu1038_7(decltype([...](...){...})) = Store[?] : &:r1038_3, r1038_6 -# 1038| v1038_8(void) = ReturnVoid : -# 1038| v1038_9(void) = AliasedUse : ~m? -# 1038| v1038_10(void) = ExitFunction : +# 1038| v1038_1(void) = EnterFunction : +# 1038| mu1038_2(unknown) = AliasedDefinition : +# 1038| r1038_3(glval) = VariableAddress : +# 1038| r1038_4(glval) = VariableAddress[#temp1038:12] : +# 1038| mu1038_5(decltype([...](...){...})) = Uninitialized[#temp1038:12] : &:r1038_4 +# 1038| r1038_6(decltype([...](...){...})) = Load[#temp1038:12] : &:r1038_4, ~m? +# 1038| mu1038_7(decltype([...](...){...})) = Store[?] : &:r1038_3, r1038_6 +# 1038| v1038_8(void) = ReturnVoid : +# 1038| v1038_9(void) = AliasedUse : ~m? +# 1038| v1038_10(void) = ExitFunction : # 1038| void (lambda [] type at line 1038, col. 12)::operator()() const # 1038| Block 0 @@ -9785,16 +9785,16 @@ ir.cpp: # 1829| char* global_string # 1829| Block 0 -# 1829| v1829_1(void) = EnterFunction : -# 1829| mu1829_2(unknown) = AliasedDefinition : -# 1829| r1829_3(glval) = VariableAddress : -# 1829| r1829_4(glval) = StringConstant : -# 1829| r1829_5(char *) = Convert : r1829_4 -# 1829| r1829_6(char *) = Convert : r1829_5 -# 1829| mu1829_7(char *) = Store[?] : &:r1829_3, r1829_6 -# 1829| v1829_8(void) = ReturnVoid : -# 1829| v1829_9(void) = AliasedUse : ~m? -# 1829| v1829_10(void) = ExitFunction : +# 1829| v1829_1(void) = EnterFunction : +# 1829| mu1829_2(unknown) = AliasedDefinition : +# 1829| r1829_3(glval) = VariableAddress : +# 1829| r1829_4(glval) = StringConstant["global string"] : +# 1829| r1829_5(char *) = Convert : r1829_4 +# 1829| r1829_6(char *) = Convert : r1829_5 +# 1829| mu1829_7(char *) = Store[?] : &:r1829_3, r1829_6 +# 1829| v1829_8(void) = ReturnVoid : +# 1829| v1829_9(void) = AliasedUse : ~m? +# 1829| v1829_10(void) = ExitFunction : perf-regression.cpp: # 6| void Big::Big() @@ -10025,7 +10025,7 @@ struct_init.cpp: # 9| r9_4(int) = Constant[0] : # 9| r9_5(glval) = PointerAdd[16] : r9_3, r9_4 # 10| r10_1(glval) = FieldAddress[name] : r9_5 -# 10| r10_2(glval) = StringConstant : +# 10| r10_2(glval) = StringConstant["1"] : # 10| r10_3(char *) = Convert : r10_2 # 10| mu10_4(char *) = Store[?] : &:r10_1, r10_3 # 10| r10_5(glval<..(*)(..)>) = FieldAddress[handler] : r9_5 @@ -10034,7 +10034,7 @@ struct_init.cpp: # 9| r9_6(int) = Constant[1] : # 9| r9_7(glval) = PointerAdd[16] : r9_3, r9_6 # 11| r11_1(glval) = FieldAddress[name] : r9_7 -# 11| r11_2(glval) = StringConstant : +# 11| r11_2(glval) = StringConstant["3"] : # 11| r11_3(char *) = Convert : r11_2 # 11| mu11_4(char *) = Store[?] : &:r11_1, r11_3 # 11| r11_5(glval<..(*)(..)>) = FieldAddress[handler] : r9_7 diff --git a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected index 94941f4a70b..28306e20866 100644 --- a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected +++ b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected @@ -71,20 +71,20 @@ test.cpp: # 10| int global01 # 10| Block 0 -# 10| v10_1(void) = EnterFunction : -# 10| m10_2(unknown) = AliasedDefinition : +# 10| v10_1(void) = EnterFunction : +# 10| m10_2(unknown) = AliasedDefinition : # 10| valnum = unique -# 10| r10_3(glval) = VariableAddress[global01] : +# 10| r10_3(glval) = VariableAddress : # 10| valnum = unique -# 10| r10_4(int) = Constant[1] : +# 10| r10_4(int) = Constant[1] : # 10| valnum = m10_5, r10_4 -# 10| m10_5(int) = Store[global01] : &:r10_3, r10_4 +# 10| m10_5(int) = Store[?] : &:r10_3, r10_4 # 10| valnum = m10_5, r10_4 -# 10| m10_6(unknown) = Chi : total:~m?, partial:m10_5 +# 10| m10_6(unknown) = Chi : total:m10_2, partial:m10_5 # 10| valnum = unique -# 10| v10_7(void) = ReturnVoid : -# 10| v10_8(void) = AliasedUse : ~m10_2 -# 10| v10_9(void) = ExitFunction : +# 10| v10_7(void) = ReturnVoid : +# 10| v10_8(void) = AliasedUse : ~m10_6 +# 10| v10_9(void) = ExitFunction : # 12| void test01(int, int) # 12| Block 0 @@ -170,20 +170,20 @@ test.cpp: # 21| int global02 # 21| Block 0 -# 21| v21_1(void) = EnterFunction : -# 21| m21_2(unknown) = AliasedDefinition : +# 21| v21_1(void) = EnterFunction : +# 21| m21_2(unknown) = AliasedDefinition : # 21| valnum = unique -# 21| r21_3(glval) = VariableAddress[global02] : +# 21| r21_3(glval) = VariableAddress : # 21| valnum = unique -# 21| r21_4(int) = Constant[2] : +# 21| r21_4(int) = Constant[2] : # 21| valnum = m21_5, r21_4 -# 21| m21_5(int) = Store[global02] : &:r21_3, r21_4 +# 21| m21_5(int) = Store[?] : &:r21_3, r21_4 # 21| valnum = m21_5, r21_4 -# 21| m21_6(unknown) = Chi : total:~m?, partial:m21_5 +# 21| m21_6(unknown) = Chi : total:m21_2, partial:m21_5 # 21| valnum = unique -# 21| v21_7(void) = ReturnVoid : -# 21| v21_8(void) = AliasedUse : ~m21_2 -# 21| v21_9(void) = ExitFunction : +# 21| v21_7(void) = ReturnVoid : +# 21| v21_8(void) = AliasedUse : ~m21_6 +# 21| v21_9(void) = ExitFunction : # 25| void test02(int, int) # 25| Block 0 @@ -276,20 +276,20 @@ test.cpp: # 35| int global03 # 35| Block 0 -# 35| v35_1(void) = EnterFunction : -# 35| m35_2(unknown) = AliasedDefinition : +# 35| v35_1(void) = EnterFunction : +# 35| m35_2(unknown) = AliasedDefinition : # 35| valnum = unique -# 35| r35_3(glval) = VariableAddress[global03] : +# 35| r35_3(glval) = VariableAddress : # 35| valnum = unique -# 35| r35_4(int) = Constant[3] : +# 35| r35_4(int) = Constant[3] : # 35| valnum = m35_5, r35_4 -# 35| m35_5(int) = Store[global03] : &:r35_3, r35_4 +# 35| m35_5(int) = Store[?] : &:r35_3, r35_4 # 35| valnum = m35_5, r35_4 -# 35| m35_6(unknown) = Chi : total:~m?, partial:m35_5 +# 35| m35_6(unknown) = Chi : total:m35_2, partial:m35_5 # 35| valnum = unique -# 35| v35_7(void) = ReturnVoid : -# 35| v35_8(void) = AliasedUse : ~m35_2 -# 35| v35_9(void) = ExitFunction : +# 35| v35_7(void) = ReturnVoid : +# 35| v35_8(void) = AliasedUse : ~m35_6 +# 35| v35_9(void) = ExitFunction : # 39| void test03(int, int, int*) # 39| Block 0 diff --git a/csharp/ql/src/experimental/ir/implementation/internal/TIRVariable.qll b/csharp/ql/src/experimental/ir/implementation/internal/TIRVariable.qll index 12a0c6e7898..fe72263249f 100644 --- a/csharp/ql/src/experimental/ir/implementation/internal/TIRVariable.qll +++ b/csharp/ql/src/experimental/ir/implementation/internal/TIRVariable.qll @@ -2,21 +2,21 @@ private import TIRVariableInternal private import Imports::TempVariableTag newtype TIRVariable = - TIRUserVariable(Language::Variable var, Language::LanguageType type, Language::Function func) { + TIRUserVariable(Language::Variable var, Language::LanguageType type, Language::Declaration func) { Construction::hasUserVariable(func, var, type) } or TIRTempVariable( - Language::Function func, Language::AST ast, TempVariableTag tag, Language::LanguageType type + Language::Declaration func, Language::AST ast, TempVariableTag tag, Language::LanguageType type ) { Construction::hasTempVariable(func, ast, tag, type) } or TIRDynamicInitializationFlag( - Language::Function func, Language::Variable var, Language::LanguageType type + Language::Declaration func, Language::Variable var, Language::LanguageType type ) { Construction::hasDynamicInitializationFlag(func, var, type) } or TIRStringLiteral( - Language::Function func, Language::AST ast, Language::LanguageType type, + Language::Declaration func, Language::AST ast, Language::LanguageType type, Language::StringLiteral literal ) { Construction::hasStringLiteral(func, ast, type, literal) diff --git a/csharp/ql/src/experimental/ir/implementation/raw/IRVariable.qll b/csharp/ql/src/experimental/ir/implementation/raw/IRVariable.qll index ca4708857a7..c92082d767d 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/IRVariable.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/IRVariable.qll @@ -18,7 +18,7 @@ private import Imports::IRType * by the AST-to-IR translation (`IRTempVariable`). */ class IRVariable extends TIRVariable { - Language::Function func; + Language::Declaration func; IRVariable() { this = TIRUserVariable(_, _, func) or @@ -79,7 +79,7 @@ class IRVariable extends TIRVariable { /** * Gets the function that references this variable. */ - final Language::Function getEnclosingFunction() { result = func } + final Language::Declaration getEnclosingFunction() { result = func } } /** @@ -246,7 +246,7 @@ class IREllipsisVariable extends IRTempVariable, IRParameter { final override string toString() { result = "#ellipsis" } - final override int getIndex() { result = func.getNumberOfParameters() } + final override int getIndex() { result = func.(Language::Function).getNumberOfParameters() } } /** diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRVariable.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRVariable.qll index ca4708857a7..c92082d767d 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRVariable.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRVariable.qll @@ -18,7 +18,7 @@ private import Imports::IRType * by the AST-to-IR translation (`IRTempVariable`). */ class IRVariable extends TIRVariable { - Language::Function func; + Language::Declaration func; IRVariable() { this = TIRUserVariable(_, _, func) or @@ -79,7 +79,7 @@ class IRVariable extends TIRVariable { /** * Gets the function that references this variable. */ - final Language::Function getEnclosingFunction() { result = func } + final Language::Declaration getEnclosingFunction() { result = func } } /** @@ -246,7 +246,7 @@ class IREllipsisVariable extends IRTempVariable, IRParameter { final override string toString() { result = "#ellipsis" } - final override int getIndex() { result = func.getNumberOfParameters() } + final override int getIndex() { result = func.(Language::Function).getNumberOfParameters() } } /** From 89d4f847312523c53ce2fa527cecba842bed9324 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Thu, 28 Apr 2022 07:59:06 +0200 Subject: [PATCH 059/505] C++: Update tests for frontend update --- .../dataflow/dataflow-tests/dataflow-ir-consistency.expected | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected index f4a3d208614..c61b0254f67 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected @@ -238,10 +238,10 @@ postWithInFlow | lambdas.cpp:20:11:20:11 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. | | lambdas.cpp:20:11:20:11 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. | | lambdas.cpp:20:11:20:11 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. | -| lambdas.cpp:23:3:23:3 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. | | lambdas.cpp:23:3:23:14 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. | | lambdas.cpp:23:3:23:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. | | lambdas.cpp:23:3:23:14 | v [post update] | PostUpdateNode should not be the target of local flow. | +| lambdas.cpp:23:15:23:15 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. | | lambdas.cpp:28:7:28:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. | | lambdas.cpp:28:10:31:2 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. | | lambdas.cpp:28:10:31:2 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. | From f0634140b638ec14a47d03bbf80454edcff0b715 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Fri, 29 Apr 2022 14:56:13 -0400 Subject: [PATCH 060/505] C++: fix inconsistencies from IR global vars --- .../cpp/ir/dataflow/internal/DataFlowUtil.qll | 16 +++++------ .../ir/implementation/raw/IRConsistency.qll | 19 +++++++++++++ .../raw/internal/IRConstruction.qll | 7 +++-- .../raw/internal/TranslatedGlobalVar.qll | 28 +++++++++++++++++++ .../dataflow-ir-consistency.expected | 20 +------------ .../ir/ir/raw_consistency.expected | 8 ++++++ 6 files changed, 69 insertions(+), 29 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll index 4171f5a5227..2bfded52441 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll @@ -100,7 +100,7 @@ class Node extends TIRDataFlowNode { Declaration getEnclosingCallable() { none() } // overridden in subclasses /** Gets the function to which this node belongs, if any. */ - Function getFunction() { none() } // overridden in subclasses + Declaration getFunction() { none() } // overridden in subclasses /** Gets the type of this node. */ IRType getType() { none() } // overridden in subclasses @@ -196,7 +196,7 @@ class InstructionNode extends Node, TInstructionNode { override Declaration getEnclosingCallable() { result = this.getFunction() } - override Function getFunction() { result = instr.getEnclosingFunction() } + override Declaration getFunction() { result = instr.getEnclosingFunction() } override IRType getType() { result = instr.getResultIRType() } @@ -222,7 +222,7 @@ class OperandNode extends Node, TOperandNode { override Declaration getEnclosingCallable() { result = this.getFunction() } - override Function getFunction() { result = op.getUse().getEnclosingFunction() } + override Declaration getFunction() { result = op.getUse().getEnclosingFunction() } override IRType getType() { result = op.getIRType() } @@ -274,7 +274,7 @@ class StoreNodeInstr extends StoreNode, TStoreNodeInstr { /** Gets the underlying instruction. */ Instruction getInstruction() { result = instr } - override Function getFunction() { result = this.getInstruction().getEnclosingFunction() } + override Declaration getFunction() { result = this.getInstruction().getEnclosingFunction() } override IRType getType() { result = this.getInstruction().getResultIRType() } @@ -328,7 +328,7 @@ class StoreNodeOperand extends StoreNode, TStoreNodeOperand { /** Gets the underlying operand. */ Operand getOperand() { result = operand } - override Function getFunction() { result = operand.getDef().getEnclosingFunction() } + override Declaration getFunction() { result = operand.getDef().getEnclosingFunction() } override IRType getType() { result = operand.getIRType() } @@ -384,7 +384,7 @@ class ReadNode extends Node, TReadNode { override Declaration getEnclosingCallable() { result = this.getFunction() } - override Function getFunction() { result = this.getInstruction().getEnclosingFunction() } + override Declaration getFunction() { result = this.getInstruction().getEnclosingFunction() } override IRType getType() { result = this.getInstruction().getResultIRType() } @@ -436,7 +436,7 @@ class SsaPhiNode extends Node, TSsaPhiNode { override Declaration getEnclosingCallable() { result = this.getFunction() } - override Function getFunction() { result = phi.getBasicBlock().getEnclosingFunction() } + override Declaration getFunction() { result = phi.getBasicBlock().getEnclosingFunction() } override IRType getType() { result instanceof IRVoidType } @@ -673,7 +673,7 @@ class VariableNode extends Node, TVariableNode { /** Gets the variable corresponding to this node. */ Variable getVariable() { result = v } - override Function getFunction() { none() } + override Declaration getFunction() { none() } override Declaration getEnclosingCallable() { // When flow crosses from one _enclosing callable_ to another, the diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRConsistency.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRConsistency.qll index 31983d34247..45b44b14a3c 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRConsistency.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRConsistency.qll @@ -524,4 +524,23 @@ module InstructionConsistency { "' has a `this` argument operand that is not an address, in function '$@'." and irFunc = getInstructionIRFunction(instr, irFuncText) } + + query predicate nonUniqueIRVariable( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(VariableInstruction vi, IRVariable v1, IRVariable v2 | + instr = vi and vi.getIRVariable() = v1 and vi.getIRVariable() = v2 and v1 != v2 + ) and + message = + "Variable instruction '" + instr.toString() + + "' has multiple associated variables, in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + or + instr.getOpcode() instanceof Opcode::VariableAddress and + not instr instanceof VariableInstruction and + message = + "Variable address instruction '" + instr.toString() + + "' has no associated variable, in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + } } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll index ddd9ab50635..c798f33c045 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll @@ -13,6 +13,7 @@ private import TranslatedElement private import TranslatedExpr private import TranslatedStmt private import TranslatedFunction +private import TranslatedGlobalVar TranslatedElement getInstructionTranslatedElement(Instruction instruction) { instruction = TRawInstruction(result, _) @@ -44,8 +45,10 @@ module Raw { } cached - predicate hasUserVariable(Function func, Variable var, CppType type) { - getTranslatedFunction(func).hasUserVariable(var, type) + predicate hasUserVariable(Declaration decl, Variable var, CppType type) { + getTranslatedFunction(decl).hasUserVariable(var, type) + or + getTranslatedVarInit(decl).hasUserVariable(var, type) } cached diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedGlobalVar.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedGlobalVar.qll index bef32ede64a..351c6b4a1cc 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedGlobalVar.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedGlobalVar.qll @@ -6,6 +6,7 @@ private import semmle.code.cpp.ir.implementation.internal.OperandTag private import semmle.code.cpp.ir.internal.CppType private import TranslatedInitialization private import InstructionTag +private import semmle.code.cpp.ir.internal.IRUtilities class TranslatedGlobalOrNamespaceVarInit extends TranslatedRootElement, TTranslatedGlobalOrNamespaceVarInit, InitializationContext { @@ -98,6 +99,33 @@ class TranslatedGlobalOrNamespaceVarInit extends TranslatedRootElement, } override Type getTargetType() { result = var.getUnspecifiedType() } + + /** + * Holds if this variable defines or accesses variable `var` with type `type`. This includes all + * parameters and local variables, plus any global variables or static data members that are + * directly accessed by the function. + */ + final predicate hasUserVariable(Variable varUsed, CppType type) { + ( + ( + varUsed instanceof GlobalOrNamespaceVariable + or + varUsed instanceof MemberVariable and not varUsed instanceof Field + ) and + exists(VariableAccess access | + access.getTarget() = varUsed and + access.getEnclosingVariable() = var + ) + or + var = varUsed + or + varUsed.(LocalScopeVariable).getEnclosingElement*() = var + or + varUsed.(Parameter).getCatchBlock().getEnclosingElement*() = var + ) and + type = getTypeForPRValue(getVariableType(varUsed)) + } + } TranslatedGlobalOrNamespaceVarInit getTranslatedVarInit(GlobalOrNamespaceVariable var) { diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected index c61b0254f67..1c802f3eeec 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected @@ -1,22 +1,4 @@ uniqueEnclosingCallable -| globals.cpp:9:5:9:19 | Address | Node should have one enclosing callable but has 0. | -| globals.cpp:9:5:9:19 | AliasedDefinition | Node should have one enclosing callable but has 0. | -| globals.cpp:9:5:9:19 | VariableAddress | Node should have one enclosing callable but has 0. | -| globals.cpp:9:5:9:19 | VariableAddress [post update] | Node should have one enclosing callable but has 0. | -| globals.cpp:9:23:9:23 | 0 | Node should have one enclosing callable but has 0. | -| globals.cpp:9:23:9:23 | ChiPartial | Node should have one enclosing callable but has 0. | -| globals.cpp:9:23:9:23 | ChiTotal | Node should have one enclosing callable but has 0. | -| globals.cpp:9:23:9:23 | Store | Node should have one enclosing callable but has 0. | -| globals.cpp:9:23:9:23 | StoreValue | Node should have one enclosing callable but has 0. | -| globals.cpp:16:12:16:26 | Address | Node should have one enclosing callable but has 0. | -| globals.cpp:16:12:16:26 | AliasedDefinition | Node should have one enclosing callable but has 0. | -| globals.cpp:16:12:16:26 | VariableAddress | Node should have one enclosing callable but has 0. | -| globals.cpp:16:12:16:26 | VariableAddress [post update] | Node should have one enclosing callable but has 0. | -| globals.cpp:16:30:16:30 | 0 | Node should have one enclosing callable but has 0. | -| globals.cpp:16:30:16:30 | ChiPartial | Node should have one enclosing callable but has 0. | -| globals.cpp:16:30:16:30 | ChiTotal | Node should have one enclosing callable but has 0. | -| globals.cpp:16:30:16:30 | Store | Node should have one enclosing callable but has 0. | -| globals.cpp:16:30:16:30 | StoreValue | Node should have one enclosing callable but has 0. | uniqueType uniqueNodeLocation | BarrierGuard.cpp:2:11:2:13 | (unnamed parameter 0) | Node should have one location but has 6. | @@ -238,10 +220,10 @@ postWithInFlow | lambdas.cpp:20:11:20:11 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. | | lambdas.cpp:20:11:20:11 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. | | lambdas.cpp:20:11:20:11 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. | +| lambdas.cpp:23:3:23:3 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. | | lambdas.cpp:23:3:23:14 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. | | lambdas.cpp:23:3:23:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. | | lambdas.cpp:23:3:23:14 | v [post update] | PostUpdateNode should not be the target of local flow. | -| lambdas.cpp:23:15:23:15 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. | | lambdas.cpp:28:7:28:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. | | lambdas.cpp:28:10:31:2 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. | | lambdas.cpp:28:10:31:2 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. | diff --git a/cpp/ql/test/library-tests/ir/ir/raw_consistency.expected b/cpp/ql/test/library-tests/ir/ir/raw_consistency.expected index 9575759051e..d987e1520b3 100644 --- a/cpp/ql/test/library-tests/ir/ir/raw_consistency.expected +++ b/cpp/ql/test/library-tests/ir/ir/raw_consistency.expected @@ -27,6 +27,14 @@ invalidOverlap nonUniqueEnclosingIRFunction fieldAddressOnNonPointer thisArgumentIsNonPointer +nonUniqueIRVariable +| ir.cpp:1038:6:1038:8 | VariableAddress: lam | Variable address instruction 'VariableAddress: lam' has no associated variable, in function '$@'. | ir.cpp:1038:6:1038:8 | (lambda [] type at line 1038, col. 12) lam | (lambda [] type at line 1038, col. 12) lam | +| ir.cpp:1759:5:1759:12 | VariableAddress: global_2 | Variable address instruction 'VariableAddress: global_2' has no associated variable, in function '$@'. | ir.cpp:1759:5:1759:12 | int global_2 | int global_2 | +| ir.cpp:1761:11:1761:18 | VariableAddress: global_3 | Variable address instruction 'VariableAddress: global_3' has no associated variable, in function '$@'. | ir.cpp:1761:11:1761:18 | int const global_3 | int const global_3 | +| ir.cpp:1763:18:1763:25 | VariableAddress: global_4 | Variable address instruction 'VariableAddress: global_4' has no associated variable, in function '$@'. | ir.cpp:1763:18:1763:25 | constructor_only global_4 | constructor_only global_4 | +| ir.cpp:1765:18:1765:25 | VariableAddress: global_5 | Variable address instruction 'VariableAddress: global_5' has no associated variable, in function '$@'. | ir.cpp:1765:18:1765:25 | constructor_only global_5 | constructor_only global_5 | +| ir.cpp:1767:7:1767:19 | VariableAddress: global_string | Variable address instruction 'VariableAddress: global_string' has no associated variable, in function '$@'. | ir.cpp:1767:7:1767:19 | char* global_string | char* global_string | +| struct_init.cpp:9:13:9:25 | VariableAddress: infos_in_file | Variable address instruction 'VariableAddress: infos_in_file' has no associated variable, in function '$@'. | struct_init.cpp:9:13:9:25 | Info infos_in_file[] | Info infos_in_file[] | missingCanonicalLanguageType multipleCanonicalLanguageTypes missingIRType From 3547c338ef95346b5561fd64ee77b5bbf1ca10fd Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Mon, 20 Jun 2022 12:00:43 -0700 Subject: [PATCH 061/505] Update docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst Co-authored-by: James Fletcher <42464962+jf205@users.noreply.github.com> --- .../codeql-cli/analyzing-databases-with-the-codeql-cli.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst b/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst index e6ee13c7823..84b76615520 100644 --- a/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst +++ b/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst @@ -160,9 +160,6 @@ of the current process. If a ``scope/name`` and ``path`` are specified, then the ``path`` cannot be absolute. It is considered relative to the root of the CodeQL pack. - -For example:: - To analyze a database using all queries in the `experimental/Security` folder within the `codeql/cpp-queries` CodeQL pack you can use:: codeql database analyze --format=sarif-latest --output=results \ From c216176de136fa831c6111588d5d987fe78913a3 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Fri, 29 Apr 2022 15:29:07 -0400 Subject: [PATCH 062/505] C++: sync and accept new consistency test --- .../aliased_ssa/IRConsistency.qll | 19 ++++ .../unaliased_ssa/IRConsistency.qll | 19 ++++ .../ir/ir/aliased_ssa_consistency.expected | 1 + .../aliased_ssa_consistency_unsound.expected | 1 + .../ir/ir/raw_consistency.expected | 7 -- .../test/library-tests/ir/ir/raw_ir.expected | 94 +++++++++---------- .../ir/ir/unaliased_ssa_consistency.expected | 1 + ...unaliased_ssa_consistency_unsound.expected | 1 + .../ir/ssa/aliased_ssa_consistency.expected | 1 + .../aliased_ssa_consistency_unsound.expected | 1 + .../ir/ssa/unaliased_ssa_consistency.expected | 1 + ...unaliased_ssa_consistency_unsound.expected | 1 + .../aliased_ssa_consistency.expected | 7 +- .../dataflow-ir-consistency.expected | 41 -------- .../syntax-zoo/raw_consistency.expected | 3 + .../unaliased_ssa_consistency.expected | 3 + .../GlobalValueNumbering/ir_gvn.expected | 54 +++++------ .../ir/implementation/raw/IRConsistency.qll | 19 ++++ .../unaliased_ssa/IRConsistency.qll | 19 ++++ .../ir/ir/raw_ir_consistency.expected | 1 + .../ir/ir/unaliased_ssa_consistency.expected | 1 + 21 files changed, 169 insertions(+), 126 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRConsistency.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRConsistency.qll index 31983d34247..45b44b14a3c 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRConsistency.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRConsistency.qll @@ -524,4 +524,23 @@ module InstructionConsistency { "' has a `this` argument operand that is not an address, in function '$@'." and irFunc = getInstructionIRFunction(instr, irFuncText) } + + query predicate nonUniqueIRVariable( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(VariableInstruction vi, IRVariable v1, IRVariable v2 | + instr = vi and vi.getIRVariable() = v1 and vi.getIRVariable() = v2 and v1 != v2 + ) and + message = + "Variable instruction '" + instr.toString() + + "' has multiple associated variables, in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + or + instr.getOpcode() instanceof Opcode::VariableAddress and + not instr instanceof VariableInstruction and + message = + "Variable address instruction '" + instr.toString() + + "' has no associated variable, in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + } } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRConsistency.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRConsistency.qll index 31983d34247..45b44b14a3c 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRConsistency.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRConsistency.qll @@ -524,4 +524,23 @@ module InstructionConsistency { "' has a `this` argument operand that is not an address, in function '$@'." and irFunc = getInstructionIRFunction(instr, irFuncText) } + + query predicate nonUniqueIRVariable( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(VariableInstruction vi, IRVariable v1, IRVariable v2 | + instr = vi and vi.getIRVariable() = v1 and vi.getIRVariable() = v2 and v1 != v2 + ) and + message = + "Variable instruction '" + instr.toString() + + "' has multiple associated variables, in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + or + instr.getOpcode() instanceof Opcode::VariableAddress and + not instr instanceof VariableInstruction and + message = + "Variable address instruction '" + instr.toString() + + "' has no associated variable, in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + } } diff --git a/cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency.expected b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency.expected index 31e5b01229c..79887fffc1f 100644 --- a/cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency.expected +++ b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency.expected @@ -23,6 +23,7 @@ invalidOverlap nonUniqueEnclosingIRFunction fieldAddressOnNonPointer thisArgumentIsNonPointer +nonUniqueIRVariable missingCanonicalLanguageType multipleCanonicalLanguageTypes missingIRType diff --git a/cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency_unsound.expected b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency_unsound.expected index 31e5b01229c..79887fffc1f 100644 --- a/cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency_unsound.expected @@ -23,6 +23,7 @@ invalidOverlap nonUniqueEnclosingIRFunction fieldAddressOnNonPointer thisArgumentIsNonPointer +nonUniqueIRVariable missingCanonicalLanguageType multipleCanonicalLanguageTypes missingIRType diff --git a/cpp/ql/test/library-tests/ir/ir/raw_consistency.expected b/cpp/ql/test/library-tests/ir/ir/raw_consistency.expected index d987e1520b3..cc50472385b 100644 --- a/cpp/ql/test/library-tests/ir/ir/raw_consistency.expected +++ b/cpp/ql/test/library-tests/ir/ir/raw_consistency.expected @@ -28,13 +28,6 @@ nonUniqueEnclosingIRFunction fieldAddressOnNonPointer thisArgumentIsNonPointer nonUniqueIRVariable -| ir.cpp:1038:6:1038:8 | VariableAddress: lam | Variable address instruction 'VariableAddress: lam' has no associated variable, in function '$@'. | ir.cpp:1038:6:1038:8 | (lambda [] type at line 1038, col. 12) lam | (lambda [] type at line 1038, col. 12) lam | -| ir.cpp:1759:5:1759:12 | VariableAddress: global_2 | Variable address instruction 'VariableAddress: global_2' has no associated variable, in function '$@'. | ir.cpp:1759:5:1759:12 | int global_2 | int global_2 | -| ir.cpp:1761:11:1761:18 | VariableAddress: global_3 | Variable address instruction 'VariableAddress: global_3' has no associated variable, in function '$@'. | ir.cpp:1761:11:1761:18 | int const global_3 | int const global_3 | -| ir.cpp:1763:18:1763:25 | VariableAddress: global_4 | Variable address instruction 'VariableAddress: global_4' has no associated variable, in function '$@'. | ir.cpp:1763:18:1763:25 | constructor_only global_4 | constructor_only global_4 | -| ir.cpp:1765:18:1765:25 | VariableAddress: global_5 | Variable address instruction 'VariableAddress: global_5' has no associated variable, in function '$@'. | ir.cpp:1765:18:1765:25 | constructor_only global_5 | constructor_only global_5 | -| ir.cpp:1767:7:1767:19 | VariableAddress: global_string | Variable address instruction 'VariableAddress: global_string' has no associated variable, in function '$@'. | ir.cpp:1767:7:1767:19 | char* global_string | char* global_string | -| struct_init.cpp:9:13:9:25 | VariableAddress: infos_in_file | Variable address instruction 'VariableAddress: infos_in_file' has no associated variable, in function '$@'. | struct_init.cpp:9:13:9:25 | Info infos_in_file[] | Info infos_in_file[] | missingCanonicalLanguageType multipleCanonicalLanguageTypes missingIRType diff --git a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected index 397f2bb288c..512bae7184d 100644 --- a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected +++ b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected @@ -5654,11 +5654,11 @@ ir.cpp: # 1038| Block 0 # 1038| v1038_1(void) = EnterFunction : # 1038| mu1038_2(unknown) = AliasedDefinition : -# 1038| r1038_3(glval) = VariableAddress : +# 1038| r1038_3(glval) = VariableAddress[lam] : # 1038| r1038_4(glval) = VariableAddress[#temp1038:12] : # 1038| mu1038_5(decltype([...](...){...})) = Uninitialized[#temp1038:12] : &:r1038_4 # 1038| r1038_6(decltype([...](...){...})) = Load[#temp1038:12] : &:r1038_4, ~m? -# 1038| mu1038_7(decltype([...](...){...})) = Store[?] : &:r1038_3, r1038_6 +# 1038| mu1038_7(decltype([...](...){...})) = Store[lam] : &:r1038_3, r1038_6 # 1038| v1038_8(void) = ReturnVoid : # 1038| v1038_9(void) = AliasedUse : ~m? # 1038| v1038_10(void) = ExitFunction : @@ -9735,31 +9735,31 @@ ir.cpp: # 1821| int global_2 # 1821| Block 0 -# 1821| v1821_1(void) = EnterFunction : -# 1821| mu1821_2(unknown) = AliasedDefinition : -# 1821| r1821_3(glval) = VariableAddress : -# 1821| r1821_4(int) = Constant[1] : -# 1821| mu1821_5(int) = Store[?] : &:r1821_3, r1821_4 -# 1821| v1821_6(void) = ReturnVoid : -# 1821| v1821_7(void) = AliasedUse : ~m? -# 1821| v1821_8(void) = ExitFunction : +# 1821| v1821_1(void) = EnterFunction : +# 1821| mu1821_2(unknown) = AliasedDefinition : +# 1821| r1821_3(glval) = VariableAddress[global_2] : +# 1821| r1821_4(int) = Constant[1] : +# 1821| mu1821_5(int) = Store[global_2] : &:r1821_3, r1821_4 +# 1821| v1821_6(void) = ReturnVoid : +# 1821| v1821_7(void) = AliasedUse : ~m? +# 1821| v1821_8(void) = ExitFunction : # 1823| int const global_3 # 1823| Block 0 -# 1823| v1823_1(void) = EnterFunction : -# 1823| mu1823_2(unknown) = AliasedDefinition : -# 1823| r1823_3(glval) = VariableAddress : -# 1823| r1823_4(int) = Constant[2] : -# 1823| mu1823_5(int) = Store[?] : &:r1823_3, r1823_4 -# 1823| v1823_6(void) = ReturnVoid : -# 1823| v1823_7(void) = AliasedUse : ~m? -# 1823| v1823_8(void) = ExitFunction : +# 1823| v1823_1(void) = EnterFunction : +# 1823| mu1823_2(unknown) = AliasedDefinition : +# 1823| r1823_3(glval) = VariableAddress[global_3] : +# 1823| r1823_4(int) = Constant[2] : +# 1823| mu1823_5(int) = Store[global_3] : &:r1823_3, r1823_4 +# 1823| v1823_6(void) = ReturnVoid : +# 1823| v1823_7(void) = AliasedUse : ~m? +# 1823| v1823_8(void) = ExitFunction : # 1825| constructor_only global_4 # 1825| Block 0 # 1825| v1825_1(void) = EnterFunction : # 1825| mu1825_2(unknown) = AliasedDefinition : -# 1825| r1825_3(glval) = VariableAddress : +# 1825| r1825_3(glval) = VariableAddress[global_4] : # 1825| r1825_4(glval) = FunctionAddress[constructor_only] : # 1825| r1825_5(int) = Constant[1] : # 1825| v1825_6(void) = Call[constructor_only] : func:r1825_4, this:r1825_3, 0:r1825_5 @@ -9773,7 +9773,7 @@ ir.cpp: # 1827| Block 0 # 1827| v1827_1(void) = EnterFunction : # 1827| mu1827_2(unknown) = AliasedDefinition : -# 1827| r1827_3(glval) = VariableAddress : +# 1827| r1827_3(glval) = VariableAddress[global_5] : # 1827| r1827_4(glval) = FunctionAddress[constructor_only] : # 1827| r1827_5(int) = Constant[2] : # 1827| v1827_6(void) = Call[constructor_only] : func:r1827_4, this:r1827_3, 0:r1827_5 @@ -9787,11 +9787,11 @@ ir.cpp: # 1829| Block 0 # 1829| v1829_1(void) = EnterFunction : # 1829| mu1829_2(unknown) = AliasedDefinition : -# 1829| r1829_3(glval) = VariableAddress : +# 1829| r1829_3(glval) = VariableAddress[global_string] : # 1829| r1829_4(glval) = StringConstant["global string"] : # 1829| r1829_5(char *) = Convert : r1829_4 # 1829| r1829_6(char *) = Convert : r1829_5 -# 1829| mu1829_7(char *) = Store[?] : &:r1829_3, r1829_6 +# 1829| mu1829_7(char *) = Store[global_string] : &:r1829_3, r1829_6 # 1829| v1829_8(void) = ReturnVoid : # 1829| v1829_9(void) = AliasedUse : ~m? # 1829| v1829_10(void) = ExitFunction : @@ -10019,31 +10019,31 @@ smart_ptr.cpp: struct_init.cpp: # 9| Info infos_in_file[] # 9| Block 0 -# 9| v9_1(void) = EnterFunction : -# 9| mu9_2(unknown) = AliasedDefinition : -# 9| r9_3(glval) = VariableAddress : -# 9| r9_4(int) = Constant[0] : -# 9| r9_5(glval) = PointerAdd[16] : r9_3, r9_4 -# 10| r10_1(glval) = FieldAddress[name] : r9_5 -# 10| r10_2(glval) = StringConstant["1"] : -# 10| r10_3(char *) = Convert : r10_2 -# 10| mu10_4(char *) = Store[?] : &:r10_1, r10_3 -# 10| r10_5(glval<..(*)(..)>) = FieldAddress[handler] : r9_5 -# 10| r10_6(..(*)(..)) = FunctionAddress[handler1] : -# 10| mu10_7(..(*)(..)) = Store[?] : &:r10_5, r10_6 -# 9| r9_6(int) = Constant[1] : -# 9| r9_7(glval) = PointerAdd[16] : r9_3, r9_6 -# 11| r11_1(glval) = FieldAddress[name] : r9_7 -# 11| r11_2(glval) = StringConstant["3"] : -# 11| r11_3(char *) = Convert : r11_2 -# 11| mu11_4(char *) = Store[?] : &:r11_1, r11_3 -# 11| r11_5(glval<..(*)(..)>) = FieldAddress[handler] : r9_7 -# 11| r11_6(glval<..()(..)>) = FunctionAddress[handler2] : -# 11| r11_7(..(*)(..)) = CopyValue : r11_6 -# 11| mu11_8(..(*)(..)) = Store[?] : &:r11_5, r11_7 -# 9| v9_8(void) = ReturnVoid : -# 9| v9_9(void) = AliasedUse : ~m? -# 9| v9_10(void) = ExitFunction : +# 9| v9_1(void) = EnterFunction : +# 9| mu9_2(unknown) = AliasedDefinition : +# 9| r9_3(glval) = VariableAddress[infos_in_file] : +# 9| r9_4(int) = Constant[0] : +# 9| r9_5(glval) = PointerAdd[16] : r9_3, r9_4 +# 10| r10_1(glval) = FieldAddress[name] : r9_5 +# 10| r10_2(glval) = StringConstant["1"] : +# 10| r10_3(char *) = Convert : r10_2 +# 10| mu10_4(char *) = Store[?] : &:r10_1, r10_3 +# 10| r10_5(glval<..(*)(..)>) = FieldAddress[handler] : r9_5 +# 10| r10_6(..(*)(..)) = FunctionAddress[handler1] : +# 10| mu10_7(..(*)(..)) = Store[?] : &:r10_5, r10_6 +# 9| r9_6(int) = Constant[1] : +# 9| r9_7(glval) = PointerAdd[16] : r9_3, r9_6 +# 11| r11_1(glval) = FieldAddress[name] : r9_7 +# 11| r11_2(glval) = StringConstant["3"] : +# 11| r11_3(char *) = Convert : r11_2 +# 11| mu11_4(char *) = Store[?] : &:r11_1, r11_3 +# 11| r11_5(glval<..(*)(..)>) = FieldAddress[handler] : r9_7 +# 11| r11_6(glval<..()(..)>) = FunctionAddress[handler2] : +# 11| r11_7(..(*)(..)) = CopyValue : r11_6 +# 11| mu11_8(..(*)(..)) = Store[?] : &:r11_5, r11_7 +# 9| v9_8(void) = ReturnVoid : +# 9| v9_9(void) = AliasedUse : ~m? +# 9| v9_10(void) = ExitFunction : # 16| void let_info_escape(Info*) # 16| Block 0 diff --git a/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency.expected b/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency.expected index 31e5b01229c..79887fffc1f 100644 --- a/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency.expected +++ b/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency.expected @@ -23,6 +23,7 @@ invalidOverlap nonUniqueEnclosingIRFunction fieldAddressOnNonPointer thisArgumentIsNonPointer +nonUniqueIRVariable missingCanonicalLanguageType multipleCanonicalLanguageTypes missingIRType diff --git a/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency_unsound.expected b/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency_unsound.expected index 31e5b01229c..79887fffc1f 100644 --- a/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency_unsound.expected @@ -23,6 +23,7 @@ invalidOverlap nonUniqueEnclosingIRFunction fieldAddressOnNonPointer thisArgumentIsNonPointer +nonUniqueIRVariable missingCanonicalLanguageType multipleCanonicalLanguageTypes missingIRType diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency.expected index 31e5b01229c..79887fffc1f 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency.expected @@ -23,6 +23,7 @@ invalidOverlap nonUniqueEnclosingIRFunction fieldAddressOnNonPointer thisArgumentIsNonPointer +nonUniqueIRVariable missingCanonicalLanguageType multipleCanonicalLanguageTypes missingIRType diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency_unsound.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency_unsound.expected index 31e5b01229c..79887fffc1f 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency_unsound.expected @@ -23,6 +23,7 @@ invalidOverlap nonUniqueEnclosingIRFunction fieldAddressOnNonPointer thisArgumentIsNonPointer +nonUniqueIRVariable missingCanonicalLanguageType multipleCanonicalLanguageTypes missingIRType diff --git a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_consistency.expected b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_consistency.expected index 31e5b01229c..79887fffc1f 100644 --- a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_consistency.expected +++ b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_consistency.expected @@ -23,6 +23,7 @@ invalidOverlap nonUniqueEnclosingIRFunction fieldAddressOnNonPointer thisArgumentIsNonPointer +nonUniqueIRVariable missingCanonicalLanguageType multipleCanonicalLanguageTypes missingIRType diff --git a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_consistency_unsound.expected b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_consistency_unsound.expected index 31e5b01229c..79887fffc1f 100644 --- a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_consistency_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_consistency_unsound.expected @@ -23,6 +23,7 @@ invalidOverlap nonUniqueEnclosingIRFunction fieldAddressOnNonPointer thisArgumentIsNonPointer +nonUniqueIRVariable missingCanonicalLanguageType multipleCanonicalLanguageTypes missingIRType diff --git a/cpp/ql/test/library-tests/syntax-zoo/aliased_ssa_consistency.expected b/cpp/ql/test/library-tests/syntax-zoo/aliased_ssa_consistency.expected index db803126364..665d30605ee 100644 --- a/cpp/ql/test/library-tests/syntax-zoo/aliased_ssa_consistency.expected +++ b/cpp/ql/test/library-tests/syntax-zoo/aliased_ssa_consistency.expected @@ -4,8 +4,6 @@ unexpectedOperand duplicateOperand missingPhiOperand missingOperandType -| cpp11.cpp:36:18:36:18 | ChiTotal | Operand 'ChiTotal' of instruction 'Chi' is missing a type in function '$@'. | cpp11.cpp:36:5:36:14 | int global_int | int global_int | -| misc.c:210:24:210:28 | ChiTotal | Operand 'ChiTotal' of instruction 'Chi' is missing a type in function '$@'. | misc.c:210:5:210:20 | int global_with_init | int global_with_init | duplicateChiOperand sideEffectWithoutPrimary instructionWithoutSuccessor @@ -93,8 +91,6 @@ useNotDominatedByDefinition switchInstructionWithoutDefaultEdge notMarkedAsConflated wronglyMarkedAsConflated -| cpp11.cpp:36:18:36:18 | Chi: 5 | Instruction 'Chi: 5' should not be marked as having a conflated result in function '$@'. | cpp11.cpp:36:5:36:14 | int global_int | int global_int | -| misc.c:210:24:210:28 | Chi: ... + ... | Instruction 'Chi: ... + ...' should not be marked as having a conflated result in function '$@'. | misc.c:210:5:210:20 | int global_with_init | int global_with_init | invalidOverlap nonUniqueEnclosingIRFunction fieldAddressOnNonPointer @@ -102,6 +98,9 @@ thisArgumentIsNonPointer | pmcallexpr.cpp:8:2:8:15 | Call: call to expression | Call instruction 'Call: call to expression' has a `this` argument operand that is not an address, in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | | pointer_to_member.cpp:23:5:23:54 | Call: call to expression | Call instruction 'Call: call to expression' has a `this` argument operand that is not an address, in function '$@'. | pointer_to_member.cpp:14:5:14:9 | int usePM(int PM::*) | int usePM(int PM::*) | | pointer_to_member.cpp:24:5:24:49 | Call: call to expression | Call instruction 'Call: call to expression' has a `this` argument operand that is not an address, in function '$@'. | pointer_to_member.cpp:14:5:14:9 | int usePM(int PM::*) | int usePM(int PM::*) | +nonUniqueIRVariable +| misc.c:178:22:178:40 | VariableAddress: __PRETTY_FUNCTION__ | Variable address instruction 'VariableAddress: __PRETTY_FUNCTION__' has no associated variable, in function '$@'. | misc.c:177:6:177:14 | void magicvars() | void magicvars() | +| misc.c:179:27:179:34 | VariableAddress: __func__ | Variable address instruction 'VariableAddress: __func__' has no associated variable, in function '$@'. | misc.c:177:6:177:14 | void magicvars() | void magicvars() | missingCanonicalLanguageType multipleCanonicalLanguageTypes missingIRType diff --git a/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected index 53bdffc3be3..e92e527e1df 100644 --- a/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected +++ b/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected @@ -1,45 +1,4 @@ uniqueEnclosingCallable -| cpp11.cpp:35:11:35:22 | Address | Node should have one enclosing callable but has 0. | -| cpp11.cpp:35:11:35:22 | AliasedDefinition | Node should have one enclosing callable but has 0. | -| cpp11.cpp:35:11:35:22 | VariableAddress | Node should have one enclosing callable but has 0. | -| cpp11.cpp:35:11:35:22 | VariableAddress [post update] | Node should have one enclosing callable but has 0. | -| cpp11.cpp:35:26:35:26 | 5 | Node should have one enclosing callable but has 0. | -| cpp11.cpp:35:26:35:26 | ChiPartial | Node should have one enclosing callable but has 0. | -| cpp11.cpp:35:26:35:26 | ChiTotal | Node should have one enclosing callable but has 0. | -| cpp11.cpp:35:26:35:26 | Store | Node should have one enclosing callable but has 0. | -| cpp11.cpp:35:26:35:26 | StoreValue | Node should have one enclosing callable but has 0. | -| cpp11.cpp:36:5:36:14 | Address | Node should have one enclosing callable but has 0. | -| cpp11.cpp:36:5:36:14 | VariableAddress | Node should have one enclosing callable but has 0. | -| cpp11.cpp:36:5:36:14 | VariableAddress [post update] | Node should have one enclosing callable but has 0. | -| cpp11.cpp:36:18:36:18 | 5 | Node should have one enclosing callable but has 0. | -| cpp11.cpp:36:18:36:18 | ChiPartial | Node should have one enclosing callable but has 0. | -| cpp11.cpp:36:18:36:18 | Store | Node should have one enclosing callable but has 0. | -| cpp11.cpp:36:18:36:18 | StoreValue | Node should have one enclosing callable but has 0. | -| misc.c:10:5:10:13 | Address | Node should have one enclosing callable but has 0. | -| misc.c:10:5:10:13 | AliasedDefinition | Node should have one enclosing callable but has 0. | -| misc.c:10:5:10:13 | VariableAddress | Node should have one enclosing callable but has 0. | -| misc.c:10:5:10:13 | VariableAddress [post update] | Node should have one enclosing callable but has 0. | -| misc.c:10:17:10:17 | 1 | Node should have one enclosing callable but has 0. | -| misc.c:10:17:10:17 | ChiPartial | Node should have one enclosing callable but has 0. | -| misc.c:10:17:10:17 | ChiTotal | Node should have one enclosing callable but has 0. | -| misc.c:10:17:10:17 | Store | Node should have one enclosing callable but has 0. | -| misc.c:10:17:10:17 | StoreValue | Node should have one enclosing callable but has 0. | -| misc.c:11:5:11:13 | Address | Node should have one enclosing callable but has 0. | -| misc.c:11:5:11:13 | AliasedDefinition | Node should have one enclosing callable but has 0. | -| misc.c:11:5:11:13 | VariableAddress | Node should have one enclosing callable but has 0. | -| misc.c:11:5:11:13 | VariableAddress [post update] | Node should have one enclosing callable but has 0. | -| misc.c:11:17:11:21 | ... + ... | Node should have one enclosing callable but has 0. | -| misc.c:11:17:11:21 | ChiPartial | Node should have one enclosing callable but has 0. | -| misc.c:11:17:11:21 | ChiTotal | Node should have one enclosing callable but has 0. | -| misc.c:11:17:11:21 | Store | Node should have one enclosing callable but has 0. | -| misc.c:11:17:11:21 | StoreValue | Node should have one enclosing callable but has 0. | -| misc.c:210:5:210:20 | Address | Node should have one enclosing callable but has 0. | -| misc.c:210:5:210:20 | VariableAddress | Node should have one enclosing callable but has 0. | -| misc.c:210:5:210:20 | VariableAddress [post update] | Node should have one enclosing callable but has 0. | -| misc.c:210:24:210:28 | ... + ... | Node should have one enclosing callable but has 0. | -| misc.c:210:24:210:28 | ChiPartial | Node should have one enclosing callable but has 0. | -| misc.c:210:24:210:28 | Store | Node should have one enclosing callable but has 0. | -| misc.c:210:24:210:28 | StoreValue | Node should have one enclosing callable but has 0. | uniqueType uniqueNodeLocation | aggregateinitializer.c:1:6:1:6 | AliasedDefinition | Node should have one location but has 20. | diff --git a/cpp/ql/test/library-tests/syntax-zoo/raw_consistency.expected b/cpp/ql/test/library-tests/syntax-zoo/raw_consistency.expected index 4a4bea025a8..69f6429c080 100644 --- a/cpp/ql/test/library-tests/syntax-zoo/raw_consistency.expected +++ b/cpp/ql/test/library-tests/syntax-zoo/raw_consistency.expected @@ -148,6 +148,9 @@ thisArgumentIsNonPointer | pmcallexpr.cpp:8:2:8:15 | Call: call to expression | Call instruction 'Call: call to expression' has a `this` argument operand that is not an address, in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | | pointer_to_member.cpp:23:5:23:54 | Call: call to expression | Call instruction 'Call: call to expression' has a `this` argument operand that is not an address, in function '$@'. | pointer_to_member.cpp:14:5:14:9 | int usePM(int PM::*) | int usePM(int PM::*) | | pointer_to_member.cpp:24:5:24:49 | Call: call to expression | Call instruction 'Call: call to expression' has a `this` argument operand that is not an address, in function '$@'. | pointer_to_member.cpp:14:5:14:9 | int usePM(int PM::*) | int usePM(int PM::*) | +nonUniqueIRVariable +| misc.c:178:22:178:40 | VariableAddress: __PRETTY_FUNCTION__ | Variable address instruction 'VariableAddress: __PRETTY_FUNCTION__' has no associated variable, in function '$@'. | misc.c:177:6:177:14 | void magicvars() | void magicvars() | +| misc.c:179:27:179:34 | VariableAddress: __func__ | Variable address instruction 'VariableAddress: __func__' has no associated variable, in function '$@'. | misc.c:177:6:177:14 | void magicvars() | void magicvars() | missingCanonicalLanguageType multipleCanonicalLanguageTypes missingIRType diff --git a/cpp/ql/test/library-tests/syntax-zoo/unaliased_ssa_consistency.expected b/cpp/ql/test/library-tests/syntax-zoo/unaliased_ssa_consistency.expected index 56e9f2e881a..83e97d12a7d 100644 --- a/cpp/ql/test/library-tests/syntax-zoo/unaliased_ssa_consistency.expected +++ b/cpp/ql/test/library-tests/syntax-zoo/unaliased_ssa_consistency.expected @@ -98,6 +98,9 @@ thisArgumentIsNonPointer | pmcallexpr.cpp:8:2:8:15 | Call: call to expression | Call instruction 'Call: call to expression' has a `this` argument operand that is not an address, in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() | | pointer_to_member.cpp:23:5:23:54 | Call: call to expression | Call instruction 'Call: call to expression' has a `this` argument operand that is not an address, in function '$@'. | pointer_to_member.cpp:14:5:14:9 | int usePM(int PM::*) | int usePM(int PM::*) | | pointer_to_member.cpp:24:5:24:49 | Call: call to expression | Call instruction 'Call: call to expression' has a `this` argument operand that is not an address, in function '$@'. | pointer_to_member.cpp:14:5:14:9 | int usePM(int PM::*) | int usePM(int PM::*) | +nonUniqueIRVariable +| misc.c:178:22:178:40 | VariableAddress: __PRETTY_FUNCTION__ | Variable address instruction 'VariableAddress: __PRETTY_FUNCTION__' has no associated variable, in function '$@'. | misc.c:177:6:177:14 | void magicvars() | void magicvars() | +| misc.c:179:27:179:34 | VariableAddress: __func__ | Variable address instruction 'VariableAddress: __func__' has no associated variable, in function '$@'. | misc.c:177:6:177:14 | void magicvars() | void magicvars() | missingCanonicalLanguageType multipleCanonicalLanguageTypes missingIRType diff --git a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected index 28306e20866..5ef74834a60 100644 --- a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected +++ b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected @@ -71,20 +71,20 @@ test.cpp: # 10| int global01 # 10| Block 0 -# 10| v10_1(void) = EnterFunction : -# 10| m10_2(unknown) = AliasedDefinition : +# 10| v10_1(void) = EnterFunction : +# 10| m10_2(unknown) = AliasedDefinition : # 10| valnum = unique -# 10| r10_3(glval) = VariableAddress : +# 10| r10_3(glval) = VariableAddress[global01] : # 10| valnum = unique -# 10| r10_4(int) = Constant[1] : +# 10| r10_4(int) = Constant[1] : # 10| valnum = m10_5, r10_4 -# 10| m10_5(int) = Store[?] : &:r10_3, r10_4 +# 10| m10_5(int) = Store[global01] : &:r10_3, r10_4 # 10| valnum = m10_5, r10_4 -# 10| m10_6(unknown) = Chi : total:m10_2, partial:m10_5 +# 10| m10_6(unknown) = Chi : total:m10_2, partial:m10_5 # 10| valnum = unique -# 10| v10_7(void) = ReturnVoid : -# 10| v10_8(void) = AliasedUse : ~m10_6 -# 10| v10_9(void) = ExitFunction : +# 10| v10_7(void) = ReturnVoid : +# 10| v10_8(void) = AliasedUse : ~m10_6 +# 10| v10_9(void) = ExitFunction : # 12| void test01(int, int) # 12| Block 0 @@ -170,20 +170,20 @@ test.cpp: # 21| int global02 # 21| Block 0 -# 21| v21_1(void) = EnterFunction : -# 21| m21_2(unknown) = AliasedDefinition : +# 21| v21_1(void) = EnterFunction : +# 21| m21_2(unknown) = AliasedDefinition : # 21| valnum = unique -# 21| r21_3(glval) = VariableAddress : +# 21| r21_3(glval) = VariableAddress[global02] : # 21| valnum = unique -# 21| r21_4(int) = Constant[2] : +# 21| r21_4(int) = Constant[2] : # 21| valnum = m21_5, r21_4 -# 21| m21_5(int) = Store[?] : &:r21_3, r21_4 +# 21| m21_5(int) = Store[global02] : &:r21_3, r21_4 # 21| valnum = m21_5, r21_4 -# 21| m21_6(unknown) = Chi : total:m21_2, partial:m21_5 +# 21| m21_6(unknown) = Chi : total:m21_2, partial:m21_5 # 21| valnum = unique -# 21| v21_7(void) = ReturnVoid : -# 21| v21_8(void) = AliasedUse : ~m21_6 -# 21| v21_9(void) = ExitFunction : +# 21| v21_7(void) = ReturnVoid : +# 21| v21_8(void) = AliasedUse : ~m21_6 +# 21| v21_9(void) = ExitFunction : # 25| void test02(int, int) # 25| Block 0 @@ -276,20 +276,20 @@ test.cpp: # 35| int global03 # 35| Block 0 -# 35| v35_1(void) = EnterFunction : -# 35| m35_2(unknown) = AliasedDefinition : +# 35| v35_1(void) = EnterFunction : +# 35| m35_2(unknown) = AliasedDefinition : # 35| valnum = unique -# 35| r35_3(glval) = VariableAddress : +# 35| r35_3(glval) = VariableAddress[global03] : # 35| valnum = unique -# 35| r35_4(int) = Constant[3] : +# 35| r35_4(int) = Constant[3] : # 35| valnum = m35_5, r35_4 -# 35| m35_5(int) = Store[?] : &:r35_3, r35_4 +# 35| m35_5(int) = Store[global03] : &:r35_3, r35_4 # 35| valnum = m35_5, r35_4 -# 35| m35_6(unknown) = Chi : total:m35_2, partial:m35_5 +# 35| m35_6(unknown) = Chi : total:m35_2, partial:m35_5 # 35| valnum = unique -# 35| v35_7(void) = ReturnVoid : -# 35| v35_8(void) = AliasedUse : ~m35_6 -# 35| v35_9(void) = ExitFunction : +# 35| v35_7(void) = ReturnVoid : +# 35| v35_8(void) = AliasedUse : ~m35_6 +# 35| v35_9(void) = ExitFunction : # 39| void test03(int, int, int*) # 39| Block 0 diff --git a/csharp/ql/src/experimental/ir/implementation/raw/IRConsistency.qll b/csharp/ql/src/experimental/ir/implementation/raw/IRConsistency.qll index 31983d34247..45b44b14a3c 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/IRConsistency.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/IRConsistency.qll @@ -524,4 +524,23 @@ module InstructionConsistency { "' has a `this` argument operand that is not an address, in function '$@'." and irFunc = getInstructionIRFunction(instr, irFuncText) } + + query predicate nonUniqueIRVariable( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(VariableInstruction vi, IRVariable v1, IRVariable v2 | + instr = vi and vi.getIRVariable() = v1 and vi.getIRVariable() = v2 and v1 != v2 + ) and + message = + "Variable instruction '" + instr.toString() + + "' has multiple associated variables, in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + or + instr.getOpcode() instanceof Opcode::VariableAddress and + not instr instanceof VariableInstruction and + message = + "Variable address instruction '" + instr.toString() + + "' has no associated variable, in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + } } diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRConsistency.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRConsistency.qll index 31983d34247..45b44b14a3c 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRConsistency.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRConsistency.qll @@ -524,4 +524,23 @@ module InstructionConsistency { "' has a `this` argument operand that is not an address, in function '$@'." and irFunc = getInstructionIRFunction(instr, irFuncText) } + + query predicate nonUniqueIRVariable( + Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText + ) { + exists(VariableInstruction vi, IRVariable v1, IRVariable v2 | + instr = vi and vi.getIRVariable() = v1 and vi.getIRVariable() = v2 and v1 != v2 + ) and + message = + "Variable instruction '" + instr.toString() + + "' has multiple associated variables, in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + or + instr.getOpcode() instanceof Opcode::VariableAddress and + not instr instanceof VariableInstruction and + message = + "Variable address instruction '" + instr.toString() + + "' has no associated variable, in function '$@'." and + irFunc = getInstructionIRFunction(instr, irFuncText) + } } diff --git a/csharp/ql/test/experimental/ir/ir/raw_ir_consistency.expected b/csharp/ql/test/experimental/ir/ir/raw_ir_consistency.expected index 7231134d5e2..05ab9037c87 100644 --- a/csharp/ql/test/experimental/ir/ir/raw_ir_consistency.expected +++ b/csharp/ql/test/experimental/ir/ir/raw_ir_consistency.expected @@ -28,6 +28,7 @@ fieldAddressOnNonPointer thisArgumentIsNonPointer | inoutref.cs:32:22:32:35 | Call: object creation of type MyStruct | Call instruction 'Call: object creation of type MyStruct' has a `this` argument operand that is not an address, in function '$@'. | inoutref.cs:29:17:29:20 | System.Void InOutRef.Main() | System.Void InOutRef.Main() | | pointers.cs:27:22:27:35 | Call: object creation of type MyStruct | Call instruction 'Call: object creation of type MyStruct' has a `this` argument operand that is not an address, in function '$@'. | pointers.cs:25:17:25:20 | System.Void Pointers.Main() | System.Void Pointers.Main() | +nonUniqueIRVariable missingCanonicalLanguageType multipleCanonicalLanguageTypes missingIRType diff --git a/csharp/ql/test/experimental/ir/ir/unaliased_ssa_consistency.expected b/csharp/ql/test/experimental/ir/ir/unaliased_ssa_consistency.expected index 7231134d5e2..05ab9037c87 100644 --- a/csharp/ql/test/experimental/ir/ir/unaliased_ssa_consistency.expected +++ b/csharp/ql/test/experimental/ir/ir/unaliased_ssa_consistency.expected @@ -28,6 +28,7 @@ fieldAddressOnNonPointer thisArgumentIsNonPointer | inoutref.cs:32:22:32:35 | Call: object creation of type MyStruct | Call instruction 'Call: object creation of type MyStruct' has a `this` argument operand that is not an address, in function '$@'. | inoutref.cs:29:17:29:20 | System.Void InOutRef.Main() | System.Void InOutRef.Main() | | pointers.cs:27:22:27:35 | Call: object creation of type MyStruct | Call instruction 'Call: object creation of type MyStruct' has a `this` argument operand that is not an address, in function '$@'. | pointers.cs:25:17:25:20 | System.Void Pointers.Main() | System.Void Pointers.Main() | +nonUniqueIRVariable missingCanonicalLanguageType multipleCanonicalLanguageTypes missingIRType From 048e5d8474c4219a6969ce1885ccbe5c4b68e109 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Wed, 23 Mar 2022 14:52:00 -0400 Subject: [PATCH 063/505] C++: IR data flow through global variables --- .../ir/dataflow/internal/DataFlowPrivate.qll | 20 ++- .../dataflow/dataflow-tests/test.cpp | 8 +- .../dataflow/taint-tests/taint.cpp | 6 +- .../UncontrolledFormatString.expected | 130 ++++++++++++++++++ 4 files changed, 156 insertions(+), 8 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll index 9dcd7f176df..e035df824e2 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll @@ -244,7 +244,25 @@ OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) { * calling context. For example, this would happen with flow through a * global or static variable. */ -predicate jumpStep(Node n1, Node n2) { none() } +predicate jumpStep(Node n1, Node n2) { + exists(GlobalOrNamespaceVariable v | + v = + n1.asInstruction() + .(StoreInstruction) + .getResultAddress() + .(VariableAddressInstruction) + .getAstVariable() and + v = n2.asVariable() + or + v = + n2.asInstruction() + .(LoadInstruction) + .getSourceAddress() + .(VariableAddressInstruction) + .getAstVariable() and + v = n1.asVariable() + ) +} /** * Holds if data can flow from `node1` to `node2` via an assignment to `f`. diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp index 5e5c5279f16..18eb893e540 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp @@ -334,19 +334,19 @@ namespace FlowThroughGlobals { } int f() { - sink(globalVar); // tainted or clean? Not sure. + sink(globalVar); // $ ir=333:17 ir=347:17 // tainted or clean? Not sure. taintGlobal(); - sink(globalVar); // $ MISSING: ast,ir + sink(globalVar); // $ ir=333:17 ir=347:17 MISSING: ast } int calledAfterTaint() { - sink(globalVar); // $ MISSING: ast,ir + sink(globalVar); // $ ir=333:17 ir=347:17 MISSING: ast } int taintAndCall() { globalVar = source(); calledAfterTaint(); - sink(globalVar); // $ ast,ir + sink(globalVar); // $ ast ir=333:17 ir=347:17 } } diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp index ebbde802ff3..945c6ff3481 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp @@ -52,9 +52,9 @@ void do_sink() sink(global4); // $ MISSING: ast,ir sink(global5); sink(global6); - sink(global7); // $ MISSING: ast,ir - sink(global8); // $ MISSING: ast,ir - sink(global9); // $ MISSING: ast,ir + sink(global7); // $ ir MISSING: ast + sink(global8); // $ ir MISSING: ast + sink(global9); // $ ir MISSING: ast sink(global10); } diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatString.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatString.expected index 7cefb7cfafc..885e188be64 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatString.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatString.expected @@ -1,4 +1,134 @@ edges +| globalVars.c:8:7:8:10 | copy | globalVars.c:27:9:27:12 | copy | +| globalVars.c:8:7:8:10 | copy | globalVars.c:27:9:27:12 | copy | +| globalVars.c:8:7:8:10 | copy | globalVars.c:27:9:27:12 | copy | +| globalVars.c:8:7:8:10 | copy | globalVars.c:30:15:30:18 | copy | +| globalVars.c:8:7:8:10 | copy | globalVars.c:30:15:30:18 | copy | +| globalVars.c:8:7:8:10 | copy | globalVars.c:30:15:30:18 | copy | +| globalVars.c:8:7:8:10 | copy | globalVars.c:33:15:33:18 | copy | +| globalVars.c:8:7:8:10 | copy | globalVars.c:35:11:35:14 | copy | +| globalVars.c:9:7:9:11 | copy2 | globalVars.c:38:9:38:13 | copy2 | +| globalVars.c:9:7:9:11 | copy2 | globalVars.c:38:9:38:13 | copy2 | +| globalVars.c:9:7:9:11 | copy2 | globalVars.c:38:9:38:13 | copy2 | +| globalVars.c:9:7:9:11 | copy2 | globalVars.c:41:15:41:19 | copy2 | +| globalVars.c:9:7:9:11 | copy2 | globalVars.c:41:15:41:19 | copy2 | +| globalVars.c:9:7:9:11 | copy2 | globalVars.c:41:15:41:19 | copy2 | +| globalVars.c:9:7:9:11 | copy2 | globalVars.c:44:15:44:19 | copy2 | +| globalVars.c:9:7:9:11 | copy2 | globalVars.c:50:9:50:13 | copy2 | +| globalVars.c:9:7:9:11 | copy2 | globalVars.c:50:9:50:13 | copy2 | +| globalVars.c:9:7:9:11 | copy2 | globalVars.c:50:9:50:13 | copy2 | +| globalVars.c:11:22:11:25 | *argv | globalVars.c:12:2:12:15 | Store | +| globalVars.c:11:22:11:25 | argv | globalVars.c:12:2:12:15 | Store | +| globalVars.c:12:2:12:15 | Store | globalVars.c:8:7:8:10 | copy | +| globalVars.c:15:21:15:23 | val | globalVars.c:16:2:16:12 | Store | +| globalVars.c:16:2:16:12 | Store | globalVars.c:9:7:9:11 | copy2 | +| globalVars.c:19:25:19:27 | *str | globalVars.c:19:25:19:27 | ReturnIndirection | +| globalVars.c:24:11:24:14 | argv | globalVars.c:11:22:11:25 | argv | +| globalVars.c:24:11:24:14 | argv | globalVars.c:24:11:24:14 | argv | +| globalVars.c:24:11:24:14 | argv | globalVars.c:24:11:24:14 | argv | +| globalVars.c:24:11:24:14 | argv | globalVars.c:24:11:24:14 | argv indirection | +| globalVars.c:24:11:24:14 | argv | globalVars.c:24:11:24:14 | argv indirection | +| globalVars.c:24:11:24:14 | argv indirection | globalVars.c:11:22:11:25 | *argv | +| globalVars.c:27:9:27:12 | copy | globalVars.c:27:9:27:12 | (const char *)... | +| globalVars.c:27:9:27:12 | copy | globalVars.c:27:9:27:12 | copy | +| globalVars.c:27:9:27:12 | copy | globalVars.c:27:9:27:12 | copy indirection | +| globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy | +| globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy | +| globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy | +| globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy indirection | +| globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy indirection | +| globalVars.c:30:15:30:18 | copy | globalVars.c:35:11:35:14 | copy | +| globalVars.c:30:15:30:18 | copy indirection | globalVars.c:19:25:19:27 | *str | +| globalVars.c:30:15:30:18 | copy indirection | globalVars.c:30:15:30:18 | printWrapper output argument | +| globalVars.c:30:15:30:18 | printWrapper output argument | globalVars.c:35:11:35:14 | copy | +| globalVars.c:33:15:33:18 | copy | globalVars.c:35:11:35:14 | copy | +| globalVars.c:35:11:35:14 | copy | globalVars.c:15:21:15:23 | val | +| globalVars.c:35:11:35:14 | copy | globalVars.c:35:11:35:14 | copy | +| globalVars.c:38:9:38:13 | copy2 | globalVars.c:38:9:38:13 | (const char *)... | +| globalVars.c:38:9:38:13 | copy2 | globalVars.c:38:9:38:13 | copy2 | +| globalVars.c:38:9:38:13 | copy2 | globalVars.c:38:9:38:13 | copy2 indirection | +| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:15:41:19 | copy2 | +| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:15:41:19 | copy2 | +| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:15:41:19 | copy2 | +| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:15:41:19 | copy2 indirection | +| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:15:41:19 | copy2 indirection | +| globalVars.c:41:15:41:19 | copy2 | globalVars.c:50:9:50:13 | (const char *)... | +| globalVars.c:41:15:41:19 | copy2 | globalVars.c:50:9:50:13 | copy2 | +| globalVars.c:41:15:41:19 | copy2 | globalVars.c:50:9:50:13 | copy2 | +| globalVars.c:41:15:41:19 | copy2 | globalVars.c:50:9:50:13 | copy2 | +| globalVars.c:41:15:41:19 | copy2 | globalVars.c:50:9:50:13 | copy2 indirection | +| globalVars.c:41:15:41:19 | copy2 indirection | globalVars.c:19:25:19:27 | *str | +| globalVars.c:41:15:41:19 | copy2 indirection | globalVars.c:41:15:41:19 | printWrapper output argument | +| globalVars.c:41:15:41:19 | printWrapper output argument | globalVars.c:50:9:50:13 | (const char *)... | +| globalVars.c:41:15:41:19 | printWrapper output argument | globalVars.c:50:9:50:13 | copy2 | +| globalVars.c:41:15:41:19 | printWrapper output argument | globalVars.c:50:9:50:13 | copy2 | +| globalVars.c:41:15:41:19 | printWrapper output argument | globalVars.c:50:9:50:13 | copy2 | +| globalVars.c:41:15:41:19 | printWrapper output argument | globalVars.c:50:9:50:13 | copy2 indirection | +| globalVars.c:44:15:44:19 | copy2 | globalVars.c:50:9:50:13 | (const char *)... | +| globalVars.c:44:15:44:19 | copy2 | globalVars.c:50:9:50:13 | copy2 | +| globalVars.c:44:15:44:19 | copy2 | globalVars.c:50:9:50:13 | copy2 | +| globalVars.c:44:15:44:19 | copy2 | globalVars.c:50:9:50:13 | copy2 | +| globalVars.c:44:15:44:19 | copy2 | globalVars.c:50:9:50:13 | copy2 indirection | +| globalVars.c:50:9:50:13 | copy2 | globalVars.c:50:9:50:13 | (const char *)... | +| globalVars.c:50:9:50:13 | copy2 | globalVars.c:50:9:50:13 | copy2 | +| globalVars.c:50:9:50:13 | copy2 | globalVars.c:50:9:50:13 | copy2 indirection | subpaths +| globalVars.c:30:15:30:18 | copy indirection | globalVars.c:19:25:19:27 | *str | globalVars.c:19:25:19:27 | ReturnIndirection | globalVars.c:30:15:30:18 | printWrapper output argument | +| globalVars.c:41:15:41:19 | copy2 indirection | globalVars.c:19:25:19:27 | *str | globalVars.c:19:25:19:27 | ReturnIndirection | globalVars.c:41:15:41:19 | printWrapper output argument | nodes +| globalVars.c:8:7:8:10 | copy | semmle.label | copy | +| globalVars.c:9:7:9:11 | copy2 | semmle.label | copy2 | +| globalVars.c:11:22:11:25 | *argv | semmle.label | *argv | +| globalVars.c:11:22:11:25 | argv | semmle.label | argv | +| globalVars.c:12:2:12:15 | Store | semmle.label | Store | +| globalVars.c:15:21:15:23 | val | semmle.label | val | +| globalVars.c:16:2:16:12 | Store | semmle.label | Store | +| globalVars.c:19:25:19:27 | *str | semmle.label | *str | +| globalVars.c:19:25:19:27 | ReturnIndirection | semmle.label | ReturnIndirection | +| globalVars.c:24:11:24:14 | argv | semmle.label | argv | +| globalVars.c:24:11:24:14 | argv | semmle.label | argv | +| globalVars.c:24:11:24:14 | argv | semmle.label | argv | +| globalVars.c:24:11:24:14 | argv indirection | semmle.label | argv indirection | +| globalVars.c:27:9:27:12 | (const char *)... | semmle.label | (const char *)... | +| globalVars.c:27:9:27:12 | (const char *)... | semmle.label | (const char *)... | +| globalVars.c:27:9:27:12 | copy | semmle.label | copy | +| globalVars.c:27:9:27:12 | copy | semmle.label | copy | +| globalVars.c:27:9:27:12 | copy | semmle.label | copy | +| globalVars.c:27:9:27:12 | copy indirection | semmle.label | copy indirection | +| globalVars.c:27:9:27:12 | copy indirection | semmle.label | copy indirection | +| globalVars.c:30:15:30:18 | copy | semmle.label | copy | +| globalVars.c:30:15:30:18 | copy | semmle.label | copy | +| globalVars.c:30:15:30:18 | copy | semmle.label | copy | +| globalVars.c:30:15:30:18 | copy indirection | semmle.label | copy indirection | +| globalVars.c:30:15:30:18 | copy indirection | semmle.label | copy indirection | +| globalVars.c:30:15:30:18 | printWrapper output argument | semmle.label | printWrapper output argument | +| globalVars.c:33:15:33:18 | copy | semmle.label | copy | +| globalVars.c:35:11:35:14 | copy | semmle.label | copy | +| globalVars.c:35:11:35:14 | copy | semmle.label | copy | +| globalVars.c:38:9:38:13 | (const char *)... | semmle.label | (const char *)... | +| globalVars.c:38:9:38:13 | (const char *)... | semmle.label | (const char *)... | +| globalVars.c:38:9:38:13 | copy2 | semmle.label | copy2 | +| globalVars.c:38:9:38:13 | copy2 | semmle.label | copy2 | +| globalVars.c:38:9:38:13 | copy2 | semmle.label | copy2 | +| globalVars.c:38:9:38:13 | copy2 indirection | semmle.label | copy2 indirection | +| globalVars.c:38:9:38:13 | copy2 indirection | semmle.label | copy2 indirection | +| globalVars.c:41:15:41:19 | copy2 | semmle.label | copy2 | +| globalVars.c:41:15:41:19 | copy2 | semmle.label | copy2 | +| globalVars.c:41:15:41:19 | copy2 | semmle.label | copy2 | +| globalVars.c:41:15:41:19 | copy2 indirection | semmle.label | copy2 indirection | +| globalVars.c:41:15:41:19 | copy2 indirection | semmle.label | copy2 indirection | +| globalVars.c:41:15:41:19 | printWrapper output argument | semmle.label | printWrapper output argument | +| globalVars.c:44:15:44:19 | copy2 | semmle.label | copy2 | +| globalVars.c:50:9:50:13 | (const char *)... | semmle.label | (const char *)... | +| globalVars.c:50:9:50:13 | (const char *)... | semmle.label | (const char *)... | +| globalVars.c:50:9:50:13 | copy2 | semmle.label | copy2 | +| globalVars.c:50:9:50:13 | copy2 | semmle.label | copy2 | +| globalVars.c:50:9:50:13 | copy2 | semmle.label | copy2 | +| globalVars.c:50:9:50:13 | copy2 indirection | semmle.label | copy2 indirection | +| globalVars.c:50:9:50:13 | copy2 indirection | semmle.label | copy2 indirection | #select +| globalVars.c:27:9:27:12 | copy | globalVars.c:24:11:24:14 | argv | globalVars.c:27:9:27:12 | copy | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | globalVars.c:24:11:24:14 | argv | argv | +| globalVars.c:30:15:30:18 | copy | globalVars.c:24:11:24:14 | argv | globalVars.c:30:15:30:18 | copy | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(str), which calls printf(format) | globalVars.c:24:11:24:14 | argv | argv | +| globalVars.c:38:9:38:13 | copy2 | globalVars.c:24:11:24:14 | argv | globalVars.c:38:9:38:13 | copy2 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | globalVars.c:24:11:24:14 | argv | argv | +| globalVars.c:41:15:41:19 | copy2 | globalVars.c:24:11:24:14 | argv | globalVars.c:41:15:41:19 | copy2 | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(str), which calls printf(format) | globalVars.c:24:11:24:14 | argv | argv | +| globalVars.c:50:9:50:13 | copy2 | globalVars.c:24:11:24:14 | argv | globalVars.c:50:9:50:13 | copy2 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | globalVars.c:24:11:24:14 | argv | argv | From a3f1d6191313eb11230c934b0b55fdd604105881 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Mon, 2 May 2022 12:07:01 -0400 Subject: [PATCH 064/505] C++: test for global var access in a global var --- .../ir/ir/aliased_ssa_consistency.expected | 1 + .../ir/ir/aliased_ssa_consistency_unsound.expected | 1 + cpp/ql/test/library-tests/ir/ir/ir.cpp | 2 ++ .../library-tests/ir/ir/operand_locations.expected | 7 +++++++ .../library-tests/ir/ir/raw_consistency.expected | 1 + cpp/ql/test/library-tests/ir/ir/raw_ir.expected | 12 ++++++++++++ .../ir/ir/unaliased_ssa_consistency.expected | 1 + .../ir/ir/unaliased_ssa_consistency_unsound.expected | 1 + 8 files changed, 26 insertions(+) diff --git a/cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency.expected b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency.expected index 79887fffc1f..65d385881e6 100644 --- a/cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency.expected +++ b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency.expected @@ -24,6 +24,7 @@ nonUniqueEnclosingIRFunction fieldAddressOnNonPointer thisArgumentIsNonPointer nonUniqueIRVariable +| ir.cpp:1831:16:1831:23 | VariableAddress: global_2 | Variable address instruction 'VariableAddress: global_2' has no associated variable, in function '$@'. | ir.cpp:1831:5:1831:12 | int global_6 | int global_6 | missingCanonicalLanguageType multipleCanonicalLanguageTypes missingIRType diff --git a/cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency_unsound.expected b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency_unsound.expected index 79887fffc1f..65d385881e6 100644 --- a/cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency_unsound.expected @@ -24,6 +24,7 @@ nonUniqueEnclosingIRFunction fieldAddressOnNonPointer thisArgumentIsNonPointer nonUniqueIRVariable +| ir.cpp:1831:16:1831:23 | VariableAddress: global_2 | Variable address instruction 'VariableAddress: global_2' has no associated variable, in function '$@'. | ir.cpp:1831:5:1831:12 | int global_6 | int global_6 | missingCanonicalLanguageType multipleCanonicalLanguageTypes missingIRType diff --git a/cpp/ql/test/library-tests/ir/ir/ir.cpp b/cpp/ql/test/library-tests/ir/ir/ir.cpp index a07036da2ba..4c49d95d453 100644 --- a/cpp/ql/test/library-tests/ir/ir/ir.cpp +++ b/cpp/ql/test/library-tests/ir/ir/ir.cpp @@ -1828,4 +1828,6 @@ constructor_only global_5 = constructor_only(2); char *global_string = "global string"; +int global_6 = global_2; + // semmle-extractor-options: -std=c++17 --clang diff --git a/cpp/ql/test/library-tests/ir/ir/operand_locations.expected b/cpp/ql/test/library-tests/ir/ir/operand_locations.expected index 58f3b25feb2..7e3252978ab 100644 --- a/cpp/ql/test/library-tests/ir/ir/operand_locations.expected +++ b/cpp/ql/test/library-tests/ir/ir/operand_locations.expected @@ -8502,6 +8502,13 @@ | ir.cpp:1829:23:1829:37 | StoreValue | r1829_6 | | ir.cpp:1829:23:1829:37 | Unary | r1829_4 | | ir.cpp:1829:23:1829:37 | Unary | r1829_5 | +| ir.cpp:1831:5:1831:12 | Address | &:r1831_3 | +| ir.cpp:1831:5:1831:12 | SideEffect | ~m1831_7 | +| ir.cpp:1831:16:1831:23 | Address | &:r1831_4 | +| ir.cpp:1831:16:1831:23 | ChiPartial | partial:m1831_6 | +| ir.cpp:1831:16:1831:23 | ChiTotal | total:m1831_2 | +| ir.cpp:1831:16:1831:23 | Load | ~m1831_2 | +| ir.cpp:1831:16:1831:23 | StoreValue | r1831_5 | | perf-regression.cpp:6:3:6:5 | Address | &:r6_5 | | perf-regression.cpp:6:3:6:5 | Address | &:r6_5 | | perf-regression.cpp:6:3:6:5 | Address | &:r6_7 | diff --git a/cpp/ql/test/library-tests/ir/ir/raw_consistency.expected b/cpp/ql/test/library-tests/ir/ir/raw_consistency.expected index cc50472385b..9d45b494ab5 100644 --- a/cpp/ql/test/library-tests/ir/ir/raw_consistency.expected +++ b/cpp/ql/test/library-tests/ir/ir/raw_consistency.expected @@ -28,6 +28,7 @@ nonUniqueEnclosingIRFunction fieldAddressOnNonPointer thisArgumentIsNonPointer nonUniqueIRVariable +| ir.cpp:1831:16:1831:23 | VariableAddress: global_2 | Variable address instruction 'VariableAddress: global_2' has no associated variable, in function '$@'. | ir.cpp:1831:5:1831:12 | int global_6 | int global_6 | missingCanonicalLanguageType multipleCanonicalLanguageTypes missingIRType diff --git a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected index 512bae7184d..87a8f921afa 100644 --- a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected +++ b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected @@ -9796,6 +9796,18 @@ ir.cpp: # 1829| v1829_9(void) = AliasedUse : ~m? # 1829| v1829_10(void) = ExitFunction : +# 1831| int global_6 +# 1831| Block 0 +# 1831| v1831_1(void) = EnterFunction : +# 1831| mu1831_2(unknown) = AliasedDefinition : +# 1831| r1831_3(glval) = VariableAddress[global_6] : +# 1831| r1831_4(glval) = VariableAddress : +# 1831| r1831_5(int) = Load[?] : &:r1831_4, ~m? +# 1831| mu1831_6(int) = Store[global_6] : &:r1831_3, r1831_5 +# 1831| v1831_7(void) = ReturnVoid : +# 1831| v1831_8(void) = AliasedUse : ~m? +# 1831| v1831_9(void) = ExitFunction : + perf-regression.cpp: # 6| void Big::Big() # 6| Block 0 diff --git a/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency.expected b/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency.expected index 79887fffc1f..65d385881e6 100644 --- a/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency.expected +++ b/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency.expected @@ -24,6 +24,7 @@ nonUniqueEnclosingIRFunction fieldAddressOnNonPointer thisArgumentIsNonPointer nonUniqueIRVariable +| ir.cpp:1831:16:1831:23 | VariableAddress: global_2 | Variable address instruction 'VariableAddress: global_2' has no associated variable, in function '$@'. | ir.cpp:1831:5:1831:12 | int global_6 | int global_6 | missingCanonicalLanguageType multipleCanonicalLanguageTypes missingIRType diff --git a/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency_unsound.expected b/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency_unsound.expected index 79887fffc1f..65d385881e6 100644 --- a/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency_unsound.expected @@ -24,6 +24,7 @@ nonUniqueEnclosingIRFunction fieldAddressOnNonPointer thisArgumentIsNonPointer nonUniqueIRVariable +| ir.cpp:1831:16:1831:23 | VariableAddress: global_2 | Variable address instruction 'VariableAddress: global_2' has no associated variable, in function '$@'. | ir.cpp:1831:5:1831:12 | int global_6 | int global_6 | missingCanonicalLanguageType multipleCanonicalLanguageTypes missingIRType From 54488eb49b69a6e29778a8496eb8ea5cca647473 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Mon, 2 May 2022 12:27:10 -0400 Subject: [PATCH 065/505] C++: fix global vars accesses in global vars --- .../cpp/ir/implementation/raw/internal/TranslatedElement.qll | 4 ++-- .../cpp/ir/implementation/raw/internal/TranslatedExpr.qll | 2 +- .../test/library-tests/ir/ir/aliased_ssa_consistency.expected | 1 - .../ir/ir/aliased_ssa_consistency_unsound.expected | 1 - cpp/ql/test/library-tests/ir/ir/raw_consistency.expected | 1 - cpp/ql/test/library-tests/ir/ir/raw_ir.expected | 4 ++-- .../library-tests/ir/ir/unaliased_ssa_consistency.expected | 1 - .../ir/ir/unaliased_ssa_consistency_unsound.expected | 1 - 8 files changed, 5 insertions(+), 10 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll index 8c53fe086a8..82663e7b125 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll @@ -25,9 +25,9 @@ private Element getRealParent(Expr expr) { result.(Destructor).getADestruction() = expr } -IRUserVariable getIRUserVariable(Function func, Variable var) { +IRUserVariable getIRUserVariable(Declaration decl, Variable var) { result.getVariable() = var and - result.getEnclosingFunction() = func + result.getEnclosingFunction() = decl } IRTempVariable getIRTempVariable(Locatable ast, TempVariableTag tag) { diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll index 5148122be05..f91486833ff 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll @@ -841,7 +841,7 @@ class TranslatedNonFieldVariableAccess extends TranslatedVariableAccess { override IRVariable getInstructionVariable(InstructionTag tag) { tag = OnlyInstructionTag() and - result = getIRUserVariable(expr.getEnclosingFunction(), expr.getTarget()) + result = getIRUserVariable(expr.getEnclosingDeclaration(), expr.getTarget()) } } diff --git a/cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency.expected b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency.expected index 65d385881e6..79887fffc1f 100644 --- a/cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency.expected +++ b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency.expected @@ -24,7 +24,6 @@ nonUniqueEnclosingIRFunction fieldAddressOnNonPointer thisArgumentIsNonPointer nonUniqueIRVariable -| ir.cpp:1831:16:1831:23 | VariableAddress: global_2 | Variable address instruction 'VariableAddress: global_2' has no associated variable, in function '$@'. | ir.cpp:1831:5:1831:12 | int global_6 | int global_6 | missingCanonicalLanguageType multipleCanonicalLanguageTypes missingIRType diff --git a/cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency_unsound.expected b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency_unsound.expected index 65d385881e6..79887fffc1f 100644 --- a/cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency_unsound.expected @@ -24,7 +24,6 @@ nonUniqueEnclosingIRFunction fieldAddressOnNonPointer thisArgumentIsNonPointer nonUniqueIRVariable -| ir.cpp:1831:16:1831:23 | VariableAddress: global_2 | Variable address instruction 'VariableAddress: global_2' has no associated variable, in function '$@'. | ir.cpp:1831:5:1831:12 | int global_6 | int global_6 | missingCanonicalLanguageType multipleCanonicalLanguageTypes missingIRType diff --git a/cpp/ql/test/library-tests/ir/ir/raw_consistency.expected b/cpp/ql/test/library-tests/ir/ir/raw_consistency.expected index 9d45b494ab5..cc50472385b 100644 --- a/cpp/ql/test/library-tests/ir/ir/raw_consistency.expected +++ b/cpp/ql/test/library-tests/ir/ir/raw_consistency.expected @@ -28,7 +28,6 @@ nonUniqueEnclosingIRFunction fieldAddressOnNonPointer thisArgumentIsNonPointer nonUniqueIRVariable -| ir.cpp:1831:16:1831:23 | VariableAddress: global_2 | Variable address instruction 'VariableAddress: global_2' has no associated variable, in function '$@'. | ir.cpp:1831:5:1831:12 | int global_6 | int global_6 | missingCanonicalLanguageType multipleCanonicalLanguageTypes missingIRType diff --git a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected index 87a8f921afa..defcc9fbc06 100644 --- a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected +++ b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected @@ -9801,8 +9801,8 @@ ir.cpp: # 1831| v1831_1(void) = EnterFunction : # 1831| mu1831_2(unknown) = AliasedDefinition : # 1831| r1831_3(glval) = VariableAddress[global_6] : -# 1831| r1831_4(glval) = VariableAddress : -# 1831| r1831_5(int) = Load[?] : &:r1831_4, ~m? +# 1831| r1831_4(glval) = VariableAddress[global_2] : +# 1831| r1831_5(int) = Load[global_2] : &:r1831_4, ~m? # 1831| mu1831_6(int) = Store[global_6] : &:r1831_3, r1831_5 # 1831| v1831_7(void) = ReturnVoid : # 1831| v1831_8(void) = AliasedUse : ~m? diff --git a/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency.expected b/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency.expected index 65d385881e6..79887fffc1f 100644 --- a/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency.expected +++ b/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency.expected @@ -24,7 +24,6 @@ nonUniqueEnclosingIRFunction fieldAddressOnNonPointer thisArgumentIsNonPointer nonUniqueIRVariable -| ir.cpp:1831:16:1831:23 | VariableAddress: global_2 | Variable address instruction 'VariableAddress: global_2' has no associated variable, in function '$@'. | ir.cpp:1831:5:1831:12 | int global_6 | int global_6 | missingCanonicalLanguageType multipleCanonicalLanguageTypes missingIRType diff --git a/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency_unsound.expected b/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency_unsound.expected index 65d385881e6..79887fffc1f 100644 --- a/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency_unsound.expected @@ -24,7 +24,6 @@ nonUniqueEnclosingIRFunction fieldAddressOnNonPointer thisArgumentIsNonPointer nonUniqueIRVariable -| ir.cpp:1831:16:1831:23 | VariableAddress: global_2 | Variable address instruction 'VariableAddress: global_2' has no associated variable, in function '$@'. | ir.cpp:1831:5:1831:12 | int global_6 | int global_6 | missingCanonicalLanguageType multipleCanonicalLanguageTypes missingIRType From 7818dafeccfc2e9262a66e58a6a2cabc0684acd5 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Mon, 2 May 2022 12:38:04 -0400 Subject: [PATCH 066/505] C++: cleanup some implicit `this` usage --- .../raw/internal/TranslatedGlobalVar.qll | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedGlobalVar.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedGlobalVar.qll index 351c6b4a1cc..93f5d4fc56d 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedGlobalVar.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedGlobalVar.qll @@ -59,25 +59,25 @@ class TranslatedGlobalOrNamespaceVarInit extends TranslatedRootElement, kind instanceof GotoEdge and ( tag = EnterFunctionTag() and - result = getInstruction(AliasedDefinitionTag()) + result = this.getInstruction(AliasedDefinitionTag()) or tag = AliasedDefinitionTag() and - result = getInstruction(InitializerVariableAddressTag()) + result = this.getInstruction(InitializerVariableAddressTag()) or tag = InitializerVariableAddressTag() and result = getChild(1).getFirstInstruction() or tag = ReturnTag() and - result = getInstruction(AliasedUseTag()) + result = this.getInstruction(AliasedUseTag()) or tag = AliasedUseTag() and - result = getInstruction(ExitFunctionTag()) + result = this.getInstruction(ExitFunctionTag()) ) } override Instruction getChildSuccessor(TranslatedElement child) { - child = getChild(1) and - result = getInstruction(ReturnTag()) + child = this.getChild(1) and + result = this.getInstruction(ReturnTag()) } final override CppType getInstructionMemoryOperandType( @@ -95,7 +95,7 @@ class TranslatedGlobalOrNamespaceVarInit extends TranslatedRootElement, } override Instruction getTargetAddress() { - result = getInstruction(InitializerVariableAddressTag()) + result = this.getInstruction(InitializerVariableAddressTag()) } override Type getTargetType() { result = var.getUnspecifiedType() } From 33910a85b920a697f048caa21688d3eacecd3b35 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Tue, 3 May 2022 16:50:53 -0400 Subject: [PATCH 067/505] C++: restrict global variable IR generation --- .../ir/implementation/raw/internal/IRConstruction.qll | 9 ++++++++- .../implementation/raw/internal/TranslatedElement.qll | 4 ++-- .../library-tests/ir/ir/operand_locations.expected | 5 ----- cpp/ql/test/library-tests/ir/ir/raw_ir.expected | 11 ----------- .../test/library-tests/ir/ssa/aliased_ssa_ir.expected | 2 -- .../ir/ssa/aliased_ssa_ir_unsound.expected | 2 -- .../library-tests/ir/ssa/unaliased_ssa_ir.expected | 2 -- .../ir/ssa/unaliased_ssa_ir_unsound.expected | 2 -- .../GlobalValueNumbering/ir_gvn.expected | 4 ---- 9 files changed, 10 insertions(+), 31 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll index c798f33c045..44e9ecbfe5e 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll @@ -37,7 +37,14 @@ module Raw { predicate functionHasIR(Function func) { exists(getTranslatedFunction(func)) } cached - predicate varHasIRFunc(GlobalOrNamespaceVariable var) { any() } // TODO: restrict? + predicate varHasIRFunc(GlobalOrNamespaceVariable var) { + var.hasInitializer() and + ( + not var.getType().isDeeplyConst() + or + var.getInitializer().getExpr() instanceof StringLiteral + ) + } cached predicate hasInstruction(TranslatedElement element, InstructionTag tag) { diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll index 82663e7b125..103b5424197 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll @@ -119,7 +119,7 @@ private predicate ignoreExprOnly(Expr expr) { exists(NewOrNewArrayExpr new | expr = new.getAllocatorCall().getArgument(0)) or not translateFunction(expr.getEnclosingFunction()) and - not expr.getEnclosingVariable() instanceof GlobalOrNamespaceVariable + not Raw::varHasIRFunc(expr.getEnclosingVariable()) or // We do not yet translate destructors properly, so for now we ignore the // destructor call. We do, however, translate the expression being @@ -665,7 +665,7 @@ newtype TTranslatedElement = } or // The side effect that initializes newly-allocated memory. TTranslatedAllocationSideEffect(AllocationExpr expr) { not ignoreSideEffects(expr) } or - TTranslatedGlobalOrNamespaceVarInit(GlobalOrNamespaceVariable var) { var.hasInitializer() } + TTranslatedGlobalOrNamespaceVarInit(GlobalOrNamespaceVariable var) { Raw::varHasIRFunc(var) } /** * Gets the index of the first explicitly initialized element in `initList` diff --git a/cpp/ql/test/library-tests/ir/ir/operand_locations.expected b/cpp/ql/test/library-tests/ir/ir/operand_locations.expected index 7e3252978ab..29d5e6cdfcb 100644 --- a/cpp/ql/test/library-tests/ir/ir/operand_locations.expected +++ b/cpp/ql/test/library-tests/ir/ir/operand_locations.expected @@ -8470,11 +8470,6 @@ | ir.cpp:1821:16:1821:16 | ChiPartial | partial:m1821_5 | | ir.cpp:1821:16:1821:16 | ChiTotal | total:m1821_2 | | ir.cpp:1821:16:1821:16 | StoreValue | r1821_4 | -| ir.cpp:1823:11:1823:18 | Address | &:r1823_3 | -| ir.cpp:1823:11:1823:18 | SideEffect | ~m1823_6 | -| ir.cpp:1823:22:1823:22 | ChiPartial | partial:m1823_5 | -| ir.cpp:1823:22:1823:22 | ChiTotal | total:m1823_2 | -| ir.cpp:1823:22:1823:22 | StoreValue | r1823_4 | | ir.cpp:1825:18:1825:25 | Address | &:r1825_3 | | ir.cpp:1825:18:1825:25 | Arg(this) | this:r1825_3 | | ir.cpp:1825:18:1825:25 | SideEffect | ~m1825_10 | diff --git a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected index defcc9fbc06..8f67435f3c1 100644 --- a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected +++ b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected @@ -9744,17 +9744,6 @@ ir.cpp: # 1821| v1821_7(void) = AliasedUse : ~m? # 1821| v1821_8(void) = ExitFunction : -# 1823| int const global_3 -# 1823| Block 0 -# 1823| v1823_1(void) = EnterFunction : -# 1823| mu1823_2(unknown) = AliasedDefinition : -# 1823| r1823_3(glval) = VariableAddress[global_3] : -# 1823| r1823_4(int) = Constant[2] : -# 1823| mu1823_5(int) = Store[global_3] : &:r1823_3, r1823_4 -# 1823| v1823_6(void) = ReturnVoid : -# 1823| v1823_7(void) = AliasedUse : ~m? -# 1823| v1823_8(void) = ExitFunction : - # 1825| constructor_only global_4 # 1825| Block 0 # 1825| v1825_1(void) = EnterFunction : diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected index 1893ab5c0d5..147c10b7c7f 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected @@ -1234,8 +1234,6 @@ ssa.cpp: # 268| v268_14(void) = AliasedUse : ~m269_7 # 268| v268_15(void) = ExitFunction : -# 274| Point* pp - # 275| void EscapedButNotConflated(bool, Point, int) # 275| Block 0 # 275| v275_1(void) = EnterFunction : diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected index faedd418ed2..396b7532d68 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected @@ -1229,8 +1229,6 @@ ssa.cpp: # 268| v268_14(void) = AliasedUse : ~m269_7 # 268| v268_15(void) = ExitFunction : -# 274| Point* pp - # 275| void EscapedButNotConflated(bool, Point, int) # 275| Block 0 # 275| v275_1(void) = EnterFunction : diff --git a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected index 6d1e8f4d03d..3fc07bf6950 100644 --- a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected +++ b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected @@ -1140,8 +1140,6 @@ ssa.cpp: # 268| v268_13(void) = AliasedUse : ~m? # 268| v268_14(void) = ExitFunction : -# 274| Point* pp - # 275| void EscapedButNotConflated(bool, Point, int) # 275| Block 0 # 275| v275_1(void) = EnterFunction : diff --git a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected index 6d1e8f4d03d..3fc07bf6950 100644 --- a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected @@ -1140,8 +1140,6 @@ ssa.cpp: # 268| v268_13(void) = AliasedUse : ~m? # 268| v268_14(void) = ExitFunction : -# 274| Point* pp - # 275| void EscapedButNotConflated(bool, Point, int) # 275| Block 0 # 275| v275_1(void) = EnterFunction : diff --git a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected index 5ef74834a60..88e365023a1 100644 --- a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected +++ b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected @@ -941,10 +941,6 @@ test.cpp: # 124| v124_13(void) = AliasedUse : m124_3 # 124| v124_14(void) = ExitFunction : -# 132| A* global_a - -# 133| int global_n - # 135| void test_read_global_same() # 135| Block 0 # 135| v135_1(void) = EnterFunction : From 5a3e546bfef78101c27a667a311c1c36da60c689 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Tue, 14 Jun 2022 11:43:05 -0400 Subject: [PATCH 068/505] C++: update test expectations --- .../dataflow/dataflow-tests/dataflow-ir-consistency.expected | 2 +- .../library-tests/syntax-zoo/dataflow-ir-consistency.expected | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected index 1c802f3eeec..b408f888383 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected @@ -220,10 +220,10 @@ postWithInFlow | lambdas.cpp:20:11:20:11 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. | | lambdas.cpp:20:11:20:11 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. | | lambdas.cpp:20:11:20:11 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. | -| lambdas.cpp:23:3:23:3 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. | | lambdas.cpp:23:3:23:14 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. | | lambdas.cpp:23:3:23:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. | | lambdas.cpp:23:3:23:14 | v [post update] | PostUpdateNode should not be the target of local flow. | +| lambdas.cpp:23:15:23:15 | (reference dereference) [post update] | PostUpdateNode should not be the target of local flow. | | lambdas.cpp:28:7:28:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. | | lambdas.cpp:28:10:31:2 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. | | lambdas.cpp:28:10:31:2 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. | diff --git a/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected index e92e527e1df..044257ed952 100644 --- a/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected +++ b/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected @@ -1622,7 +1622,6 @@ postWithInFlow | cpp11.cpp:28:21:28:34 | temporary object [post update] | PostUpdateNode should not be the target of local flow. | | cpp11.cpp:29:7:29:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. | | cpp11.cpp:31:5:31:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. | -| cpp11.cpp:35:11:35:22 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. | | cpp11.cpp:36:5:36:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. | | cpp11.cpp:56:14:56:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. | | cpp11.cpp:56:14:56:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. | From 8b47b838cae89ca20db7d678dec8e12761f6796d Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Tue, 14 Jun 2022 15:59:47 -0400 Subject: [PATCH 069/505] C++: autoformat --- .../cpp/ir/implementation/raw/internal/TranslatedGlobalVar.qll | 1 - 1 file changed, 1 deletion(-) diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedGlobalVar.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedGlobalVar.qll index 93f5d4fc56d..31174d8ba5f 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedGlobalVar.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedGlobalVar.qll @@ -125,7 +125,6 @@ class TranslatedGlobalOrNamespaceVarInit extends TranslatedRootElement, ) and type = getTypeForPRValue(getVariableType(varUsed)) } - } TranslatedGlobalOrNamespaceVarInit getTranslatedVarInit(GlobalOrNamespaceVariable var) { From d28c39cd734861e9e3135d350192262cd153a5e3 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Wed, 15 Jun 2022 15:00:37 -0400 Subject: [PATCH 070/505] C++: update test expectations --- cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp index 945c6ff3481..2ae093098d2 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp @@ -47,9 +47,9 @@ void do_source() void do_sink() { sink(global1); - sink(global2); // $ MISSING: ast,ir - sink(global3); // $ MISSING: ast,ir - sink(global4); // $ MISSING: ast,ir + sink(global2); // $ ir MISSING: ast + sink(global3); // $ ir MISSING: ast + sink(global4); // $ ir MISSING: ast sink(global5); sink(global6); sink(global7); // $ ir MISSING: ast From 73b657ce2595a0d52763b832febf06b173e225e5 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 21 Jun 2022 12:23:20 +0200 Subject: [PATCH 071/505] QL: focus alert locations --- ql/ql/src/codeql_ql/ast/Ast.qll | 22 ++++- .../performance/VarUnusedInDisjunct.ql | 20 ++--- ql/ql/src/queries/style/LibraryAnnotation.ql | 7 +- .../queries/style/UseInstanceofExtension.ql | 2 +- ql/ql/test/callgraph/callgraph.expected | 80 +++++++++---------- ql/ql/test/printAst/printAst.expected | 68 ++++++++-------- .../AcronymsShouldBeCamelCase.expected | 6 +- .../queries/style/DeadCode/DeadCode.expected | 4 +- .../MissingOverride/MissingOverride.expected | 2 +- .../style/Misspelling/Misspelling.expected | 6 +- ql/ql/test/type/type.expected | 36 ++++----- 11 files changed, 136 insertions(+), 117 deletions(-) diff --git a/ql/ql/src/codeql_ql/ast/Ast.qll b/ql/ql/src/codeql_ql/ast/Ast.qll index fa551b0de83..d12239de072 100644 --- a/ql/ql/src/codeql_ql/ast/Ast.qll +++ b/ql/ql/src/codeql_ql/ast/Ast.qll @@ -21,11 +21,13 @@ private string stringIndexedMember(string name, string index) { class AstNode extends TAstNode { string toString() { result = this.getAPrimaryQlClass() } - /** - * Gets the location of the AST node. - */ + /** Gets the location of the AST node. */ cached - Location getLocation() { + Location getLocation() { result = this.getFullLocation() } // overriden in some subclasses + + /** Gets the location that spans the entire AST node. */ + cached + final Location getFullLocation() { exists(QL::AstNode node | not node instanceof QL::ParExpr | node = toQL(this) and result = node.getLocation() @@ -434,6 +436,8 @@ class ClasslessPredicate extends TClasslessPredicate, Predicate, ModuleDeclarati ClasslessPredicate() { this = TClasslessPredicate(pred) } + override Location getLocation() { result = pred.getName().getLocation() } + /** * Gets the aliased value if this predicate is an alias * E.g. for `predicate foo = Module::bar/2;` gets `Module::bar/2`. @@ -484,6 +488,8 @@ class ClassPredicate extends TClassPredicate, Predicate { ClassPredicate() { this = TClassPredicate(pred) } + override Location getLocation() { result = pred.getName().getLocation() } + override string getName() { result = pred.getName().getValue() } override Formula getBody() { toQL(result) = pred.getChild(_).(QL::Body).getChild() } @@ -701,6 +707,8 @@ class Module extends TModule, ModuleDeclaration { Module() { this = TModule(mod) } + override Location getLocation() { result = mod.getName().getLocation() } + override string getAPrimaryQlClass() { result = "Module" } override string getName() { result = mod.getName().getChild().getValue() } @@ -784,6 +792,8 @@ class Class extends TClass, TypeDeclaration, ModuleDeclaration { Class() { this = TClass(cls) } + override Location getLocation() { result = cls.getName().getLocation() } + override string getAPrimaryQlClass() { result = "Class" } override string getName() { result = cls.getName().getValue() } @@ -871,6 +881,8 @@ class NewType extends TNewType, TypeDeclaration, ModuleDeclaration { NewType() { this = TNewType(type) } + override Location getLocation() { result = type.getName().getLocation() } + override string getName() { result = type.getName().getValue() } override string getAPrimaryQlClass() { result = "NewType" } @@ -896,6 +908,8 @@ class NewTypeBranch extends TNewTypeBranch, Predicate, TypeDeclaration { NewTypeBranch() { this = TNewTypeBranch(branch) } + override Location getLocation() { result = branch.getName().getLocation() } + override string getAPrimaryQlClass() { result = "NewTypeBranch" } override string getName() { result = branch.getName().getValue() } diff --git a/ql/ql/src/queries/performance/VarUnusedInDisjunct.ql b/ql/ql/src/queries/performance/VarUnusedInDisjunct.ql index 2317cdc80c4..c26b47554fe 100644 --- a/ql/ql/src/queries/performance/VarUnusedInDisjunct.ql +++ b/ql/ql/src/queries/performance/VarUnusedInDisjunct.ql @@ -119,12 +119,14 @@ class EffectiveDisjunction extends AstNode { * Holds if `disj` only uses `var` in one of its branches. */ pragma[noinline] -predicate onlyUseInOneBranch(EffectiveDisjunction disj, VarDef var) { +predicate onlyUseInOneBranch(EffectiveDisjunction disj, VarDef var, AstNode notBoundIn) { alwaysBindsVar(var, disj.getLeft()) and - not alwaysBindsVar(var, disj.getRight()) + not alwaysBindsVar(var, disj.getRight()) and + notBoundIn = disj.getRight() or not alwaysBindsVar(var, disj.getLeft()) and - alwaysBindsVar(var, disj.getRight()) + alwaysBindsVar(var, disj.getRight()) and + notBoundIn = disj.getLeft() } /** @@ -170,7 +172,7 @@ class EffectiveConjunction extends AstNode { predicate varIsAlwaysBound(VarDef var, AstNode node) { // base case alwaysBindsVar(var, node) and - onlyUseInOneBranch(_, var) // <- manual magic + onlyUseInOneBranch(_, var, _) // <- manual magic or // recursive cases exists(AstNode parent | node.getParent() = parent | varIsAlwaysBound(var, parent)) @@ -194,8 +196,8 @@ predicate varIsAlwaysBound(VarDef var, AstNode node) { * Holds if `disj` only uses `var` in one of its branches. * And we should report it as being a bad thing. */ -predicate badDisjunction(EffectiveDisjunction disj, VarDef var) { - onlyUseInOneBranch(disj, var) and +predicate badDisjunction(EffectiveDisjunction disj, VarDef var, AstNode notBoundIn) { + onlyUseInOneBranch(disj, var, notBoundIn) and // it's fine if it's always bound further up not varIsAlwaysBound(var, disj) and // none() on one side makes everything fine. (this happens, it's a type-system hack) @@ -213,9 +215,9 @@ predicate badDisjunction(EffectiveDisjunction disj, VarDef var) { not isTinyAssignment(disj.getAnOperand()) } -from EffectiveDisjunction disj, VarDef var, string type +from EffectiveDisjunction disj, VarDef var, AstNode notBoundIn, string type where - badDisjunction(disj, var) and - not badDisjunction(disj.getParent(), var) and // avoid duplicate reporting of the same error + badDisjunction(disj, var, notBoundIn) and + not badDisjunction(disj.getParent(), var, _) and // avoid duplicate reporting of the same error if var.getParent() instanceof FieldDecl then type = "field" else type = "variable" select disj, "The $@ is only used in one side of disjunct.", var, type + " " + var.getName() diff --git a/ql/ql/src/queries/style/LibraryAnnotation.ql b/ql/ql/src/queries/style/LibraryAnnotation.ql index cf4a4bc8232..99b80f97fb9 100644 --- a/ql/ql/src/queries/style/LibraryAnnotation.ql +++ b/ql/ql/src/queries/style/LibraryAnnotation.ql @@ -10,6 +10,9 @@ import ql -from AstNode n -where n.hasAnnotation("library") and not n.hasAnnotation("deprecated") +from AstNode n, Annotation library +where + library = n.getAnAnnotation() and + library.getName() = "library" and + not n.hasAnnotation("deprecated") select n, "Don't use the library annotation." diff --git a/ql/ql/src/queries/style/UseInstanceofExtension.ql b/ql/ql/src/queries/style/UseInstanceofExtension.ql index 2e7b8e3de80..da27e5cbb47 100644 --- a/ql/ql/src/queries/style/UseInstanceofExtension.ql +++ b/ql/ql/src/queries/style/UseInstanceofExtension.ql @@ -18,4 +18,4 @@ where usesFieldBasedInstanceof(c, any(TypeExpr te | te.getResolvedType() = type), _, _) ) and message = "consider defining $@ as non-extending subtype of $@" -select c, message, c, c.getName(), type, type.getName() +select c, message, c, c.getName(), type.getDeclaration(), type.getName() diff --git a/ql/ql/test/callgraph/callgraph.expected b/ql/ql/test/callgraph/callgraph.expected index 97c84034602..dae7a03b047 100644 --- a/ql/ql/test/callgraph/callgraph.expected +++ b/ql/ql/test/callgraph/callgraph.expected @@ -1,47 +1,47 @@ getTarget -| Bar.qll:5:38:5:47 | PredicateCall | Bar.qll:8:3:8:31 | ClasslessPredicate snapshot | -| Bar.qll:24:12:24:32 | MemberCall | Bar.qll:19:3:19:47 | ClassPredicate getParameter | -| Bar.qll:26:12:26:31 | MemberCall | Bar.qll:19:3:19:47 | ClassPredicate getParameter | -| Bar.qll:30:12:30:32 | MemberCall | Bar.qll:19:3:19:47 | ClassPredicate getParameter | -| Baz.qll:8:18:8:44 | MemberCall | Baz.qll:4:3:4:37 | ClassPredicate getImportedPath | -| Foo.qll:5:26:5:30 | PredicateCall | Foo.qll:3:1:3:26 | ClasslessPredicate foo | -| Foo.qll:10:21:10:25 | PredicateCall | Foo.qll:8:3:8:28 | ClassPredicate bar | -| Foo.qll:14:30:14:40 | MemberCall | Foo.qll:10:3:10:27 | ClassPredicate baz | -| Foo.qll:17:27:17:42 | MemberCall | Foo.qll:8:3:8:28 | ClassPredicate bar | -| Foo.qll:29:5:29:16 | PredicateCall | Foo.qll:20:3:20:54 | ClasslessPredicate myThing2 | -| Foo.qll:29:5:29:16 | PredicateCall | Foo.qll:26:3:26:32 | ClasslessPredicate alias2 | -| Foo.qll:31:5:31:12 | PredicateCall | Foo.qll:22:3:22:32 | ClasslessPredicate myThing0 | -| Foo.qll:31:5:31:12 | PredicateCall | Foo.qll:24:3:24:32 | ClasslessPredicate alias0 | +| Bar.qll:5:38:5:47 | PredicateCall | Bar.qll:8:7:8:14 | ClasslessPredicate snapshot | +| Bar.qll:24:12:24:32 | MemberCall | Bar.qll:19:7:19:18 | ClassPredicate getParameter | +| Bar.qll:26:12:26:31 | MemberCall | Bar.qll:19:7:19:18 | ClassPredicate getParameter | +| Bar.qll:30:12:30:32 | MemberCall | Bar.qll:19:7:19:18 | ClassPredicate getParameter | +| Baz.qll:8:18:8:44 | MemberCall | Baz.qll:4:10:4:24 | ClassPredicate getImportedPath | +| Foo.qll:5:26:5:30 | PredicateCall | Foo.qll:3:11:3:13 | ClasslessPredicate foo | +| Foo.qll:10:21:10:25 | PredicateCall | Foo.qll:8:13:8:15 | ClassPredicate bar | +| Foo.qll:14:30:14:40 | MemberCall | Foo.qll:10:13:10:15 | ClassPredicate baz | +| Foo.qll:17:27:17:42 | MemberCall | Foo.qll:8:13:8:15 | ClassPredicate bar | +| Foo.qll:29:5:29:16 | PredicateCall | Foo.qll:20:13:20:20 | ClasslessPredicate myThing2 | +| Foo.qll:29:5:29:16 | PredicateCall | Foo.qll:26:13:26:18 | ClasslessPredicate alias2 | +| Foo.qll:31:5:31:12 | PredicateCall | Foo.qll:22:13:22:20 | ClasslessPredicate myThing0 | +| Foo.qll:31:5:31:12 | PredicateCall | Foo.qll:24:13:24:18 | ClasslessPredicate alias0 | | Foo.qll:36:36:36:65 | MemberCall | file://:0:0:0:0 | replaceAll | | Foo.qll:38:39:38:67 | MemberCall | file://:0:0:0:0 | regexpCapture | -| Overrides.qll:8:30:8:39 | MemberCall | Overrides.qll:6:3:6:29 | ClassPredicate bar | -| Overrides.qll:16:39:16:48 | MemberCall | Overrides.qll:6:3:6:29 | ClassPredicate bar | -| Overrides.qll:16:39:16:48 | MemberCall | Overrides.qll:14:12:14:43 | ClassPredicate bar | -| Overrides.qll:24:39:24:48 | MemberCall | Overrides.qll:6:3:6:29 | ClassPredicate bar | -| Overrides.qll:24:39:24:48 | MemberCall | Overrides.qll:22:12:22:44 | ClassPredicate bar | -| Overrides.qll:28:3:28:9 | MemberCall | Overrides.qll:6:3:6:29 | ClassPredicate bar | -| Overrides.qll:29:3:29:10 | MemberCall | Overrides.qll:8:3:8:41 | ClassPredicate baz | -| ParamModules.qll:5:28:5:41 | PredicateCall | ParamModules.qll:2:13:2:36 | ClasslessPredicate fooSig | -| ParamModules.qll:5:28:5:41 | PredicateCall | ParamModules.qll:8:3:8:35 | ClasslessPredicate myFoo | -| ParamModules.qll:10:26:10:49 | PredicateCall | ParamModules.qll:5:5:5:43 | ClasslessPredicate bar | -| ParamModules.qll:26:27:26:53 | PredicateCall | ParamModules.qll:17:5:17:42 | ClasslessPredicate getAnEven | -| ParamModules.qll:26:27:26:61 | MemberCall | ParamModules.qll:23:5:23:39 | ClassPredicate myFoo | -| ParamModules.qll:37:29:37:47 | PredicateCall | ParamModules.qll:33:5:33:17 | ClasslessPredicate getThing | -| ParamModules.qll:37:29:37:47 | PredicateCall | ParamModules.qll:51:5:51:26 | ClasslessPredicate getThing | -| ParamModules.qll:59:30:59:45 | PredicateCall | ParamModules.qll:37:5:37:49 | ClasslessPredicate getThing | -| ParamModules.qll:59:30:59:53 | MemberCall | ParamModules.qll:46:7:46:41 | ClassPredicate myFoo | -| ParamModules.qll:61:39:61:47 | MemberCall | ParamModules.qll:46:7:46:41 | ClassPredicate myFoo | -| packs/other/OtherThing.qll:5:3:5:8 | PredicateCall | packs/lib/LibThing/Foo.qll:1:1:1:30 | ClasslessPredicate foo | -| packs/other/OtherThing.qll:6:3:6:8 | PredicateCall | packs/src/SrcThing.qll:8:1:8:30 | ClasslessPredicate bar | -| packs/src/SrcThing.qll:4:3:4:8 | PredicateCall | packs/lib/LibThing/Foo.qll:1:1:1:30 | ClasslessPredicate foo | -| packs/src/SrcThing.qll:5:3:5:8 | PredicateCall | packs/src/SrcThing.qll:8:1:8:30 | ClasslessPredicate bar | +| Overrides.qll:8:30:8:39 | MemberCall | Overrides.qll:6:7:6:9 | ClassPredicate bar | +| Overrides.qll:16:39:16:48 | MemberCall | Overrides.qll:6:7:6:9 | ClassPredicate bar | +| Overrides.qll:16:39:16:48 | MemberCall | Overrides.qll:14:16:14:18 | ClassPredicate bar | +| Overrides.qll:24:39:24:48 | MemberCall | Overrides.qll:6:7:6:9 | ClassPredicate bar | +| Overrides.qll:24:39:24:48 | MemberCall | Overrides.qll:22:16:22:18 | ClassPredicate bar | +| Overrides.qll:28:3:28:9 | MemberCall | Overrides.qll:6:7:6:9 | ClassPredicate bar | +| Overrides.qll:29:3:29:10 | MemberCall | Overrides.qll:8:13:8:15 | ClassPredicate baz | +| ParamModules.qll:5:28:5:41 | PredicateCall | ParamModules.qll:2:23:2:28 | ClasslessPredicate fooSig | +| ParamModules.qll:5:28:5:41 | PredicateCall | ParamModules.qll:8:13:8:17 | ClasslessPredicate myFoo | +| ParamModules.qll:10:26:10:49 | PredicateCall | ParamModules.qll:5:15:5:17 | ClasslessPredicate bar | +| ParamModules.qll:26:27:26:53 | PredicateCall | ParamModules.qll:17:13:17:21 | ClasslessPredicate getAnEven | +| ParamModules.qll:26:27:26:61 | MemberCall | ParamModules.qll:23:12:23:16 | ClassPredicate myFoo | +| ParamModules.qll:37:29:37:47 | PredicateCall | ParamModules.qll:33:7:33:14 | ClasslessPredicate getThing | +| ParamModules.qll:37:29:37:47 | PredicateCall | ParamModules.qll:51:7:51:14 | ClasslessPredicate getThing | +| ParamModules.qll:59:30:59:45 | PredicateCall | ParamModules.qll:37:7:37:14 | ClasslessPredicate getThing | +| ParamModules.qll:59:30:59:53 | MemberCall | ParamModules.qll:46:14:46:18 | ClassPredicate myFoo | +| ParamModules.qll:61:39:61:47 | MemberCall | ParamModules.qll:46:14:46:18 | ClassPredicate myFoo | +| packs/other/OtherThing.qll:5:3:5:8 | PredicateCall | packs/lib/LibThing/Foo.qll:1:11:1:13 | ClasslessPredicate foo | +| packs/other/OtherThing.qll:6:3:6:8 | PredicateCall | packs/src/SrcThing.qll:8:11:8:13 | ClasslessPredicate bar | +| packs/src/SrcThing.qll:4:3:4:8 | PredicateCall | packs/lib/LibThing/Foo.qll:1:11:1:13 | ClasslessPredicate foo | +| packs/src/SrcThing.qll:5:3:5:8 | PredicateCall | packs/src/SrcThing.qll:8:11:8:13 | ClasslessPredicate bar | dependsOn | packs/other/qlpack.yml:1:1:1:4 | ql-other-pack-thing | packs/src/qlpack.yml:1:1:1:4 | ql-testing-src-pack | | packs/src/qlpack.yml:1:1:1:4 | ql-testing-src-pack | packs/lib/qlpack.yml:1:1:1:4 | ql-testing-lib-pack | exprPredicate -| Foo.qll:24:22:24:31 | predicate | Foo.qll:22:3:22:32 | ClasslessPredicate myThing0 | -| Foo.qll:26:22:26:31 | predicate | Foo.qll:20:3:20:54 | ClasslessPredicate myThing2 | -| Foo.qll:47:55:47:62 | predicate | Foo.qll:42:20:42:27 | NewTypeBranch MkRoot | -| Foo.qll:47:65:47:70 | predicate | Foo.qll:44:9:44:56 | ClasslessPredicate edge | -| ParamModules.qll:4:18:4:25 | predicate | ParamModules.qll:2:13:2:36 | ClasslessPredicate fooSig | -| ParamModules.qll:10:34:10:40 | predicate | ParamModules.qll:8:3:8:35 | ClasslessPredicate myFoo | +| Foo.qll:24:22:24:31 | predicate | Foo.qll:22:13:22:20 | ClasslessPredicate myThing0 | +| Foo.qll:26:22:26:31 | predicate | Foo.qll:20:13:20:20 | ClasslessPredicate myThing2 | +| Foo.qll:47:55:47:62 | predicate | Foo.qll:42:20:42:25 | NewTypeBranch MkRoot | +| Foo.qll:47:65:47:70 | predicate | Foo.qll:44:19:44:22 | ClasslessPredicate edge | +| ParamModules.qll:4:18:4:25 | predicate | ParamModules.qll:2:23:2:28 | ClasslessPredicate fooSig | +| ParamModules.qll:10:34:10:40 | predicate | ParamModules.qll:8:13:8:17 | ClasslessPredicate myFoo | diff --git a/ql/ql/test/printAst/printAst.expected b/ql/ql/test/printAst/printAst.expected index 603608355d7..daa299215d3 100644 --- a/ql/ql/test/printAst/printAst.expected +++ b/ql/ql/test/printAst/printAst.expected @@ -3,8 +3,8 @@ nodes | Foo.qll:1:1:1:17 | Import | semmle.order | 1 | | Foo.qll:1:1:27:2 | TopLevel | semmle.label | [TopLevel] TopLevel | | Foo.qll:1:1:27:2 | TopLevel | semmle.order | 1 | -| Foo.qll:3:1:7:1 | Class Foo | semmle.label | [Class] Class Foo | -| Foo.qll:3:1:7:1 | Class Foo | semmle.order | 3 | +| Foo.qll:3:7:3:9 | Class Foo | semmle.label | [Class] Class Foo | +| Foo.qll:3:7:3:9 | Class Foo | semmle.order | 3 | | Foo.qll:3:19:3:22 | TypeExpr | semmle.label | [TypeExpr] TypeExpr | | Foo.qll:3:19:3:22 | TypeExpr | semmle.order | 4 | | Foo.qll:4:3:4:17 | CharPred Foo | semmle.label | [CharPred] CharPred Foo | @@ -17,8 +17,8 @@ nodes | Foo.qll:4:15:4:15 | Integer | semmle.order | 8 | | Foo.qll:6:3:6:8 | TypeExpr | semmle.label | [TypeExpr] TypeExpr | | Foo.qll:6:3:6:8 | TypeExpr | semmle.order | 9 | -| Foo.qll:6:3:6:38 | ClassPredicate toString | semmle.label | [ClassPredicate] ClassPredicate toString | -| Foo.qll:6:3:6:38 | ClassPredicate toString | semmle.order | 9 | +| Foo.qll:6:10:6:17 | ClassPredicate toString | semmle.label | [ClassPredicate] ClassPredicate toString | +| Foo.qll:6:10:6:17 | ClassPredicate toString | semmle.order | 10 | | Foo.qll:6:23:6:28 | result | semmle.label | [ResultAccess] result | | Foo.qll:6:23:6:28 | result | semmle.order | 11 | | Foo.qll:6:23:6:36 | ComparisonFormula | semmle.label | [ComparisonFormula] ComparisonFormula | @@ -27,8 +27,8 @@ nodes | Foo.qll:6:32:6:36 | String | semmle.order | 13 | | Foo.qll:9:1:9:5 | annotation | semmle.label | [Annotation] annotation | | Foo.qll:9:1:9:5 | annotation | semmle.order | 14 | -| Foo.qll:9:7:11:1 | ClasslessPredicate foo | semmle.label | [ClasslessPredicate] ClasslessPredicate foo | -| Foo.qll:9:7:11:1 | ClasslessPredicate foo | semmle.order | 15 | +| Foo.qll:9:17:9:19 | ClasslessPredicate foo | semmle.label | [ClasslessPredicate] ClasslessPredicate foo | +| Foo.qll:9:17:9:19 | ClasslessPredicate foo | semmle.order | 15 | | Foo.qll:9:21:9:23 | TypeExpr | semmle.label | [TypeExpr] TypeExpr | | Foo.qll:9:21:9:23 | TypeExpr | semmle.order | 16 | | Foo.qll:9:21:9:25 | f | semmle.label | [VarDecl] f | @@ -59,8 +59,8 @@ nodes | Foo.qll:10:69:10:73 | inner | semmle.order | 29 | | Foo.qll:10:69:10:84 | MemberCall | semmle.label | [MemberCall] MemberCall | | Foo.qll:10:69:10:84 | MemberCall | semmle.order | 29 | -| Foo.qll:13:1:27:1 | ClasslessPredicate calls | semmle.label | [ClasslessPredicate] ClasslessPredicate calls | -| Foo.qll:13:1:27:1 | ClasslessPredicate calls | semmle.order | 31 | +| Foo.qll:13:11:13:15 | ClasslessPredicate calls | semmle.label | [ClasslessPredicate] ClasslessPredicate calls | +| Foo.qll:13:11:13:15 | ClasslessPredicate calls | semmle.order | 31 | | Foo.qll:13:17:13:19 | TypeExpr | semmle.label | [TypeExpr] TypeExpr | | Foo.qll:13:17:13:19 | TypeExpr | semmle.order | 32 | | Foo.qll:13:17:13:21 | f | semmle.label | [VarDecl] f | @@ -239,38 +239,38 @@ nodes edges | Foo.qll:1:1:27:2 | TopLevel | Foo.qll:1:1:1:17 | Import | semmle.label | getAnImport() | | Foo.qll:1:1:27:2 | TopLevel | Foo.qll:1:1:1:17 | Import | semmle.order | 1 | -| Foo.qll:1:1:27:2 | TopLevel | Foo.qll:3:1:7:1 | Class Foo | semmle.label | getAClass() | -| Foo.qll:1:1:27:2 | TopLevel | Foo.qll:3:1:7:1 | Class Foo | semmle.order | 3 | -| Foo.qll:1:1:27:2 | TopLevel | Foo.qll:9:7:11:1 | ClasslessPredicate foo | semmle.label | getAPredicate() | -| Foo.qll:1:1:27:2 | TopLevel | Foo.qll:9:7:11:1 | ClasslessPredicate foo | semmle.order | 15 | -| Foo.qll:1:1:27:2 | TopLevel | Foo.qll:13:1:27:1 | ClasslessPredicate calls | semmle.label | getAPredicate() | -| Foo.qll:1:1:27:2 | TopLevel | Foo.qll:13:1:27:1 | ClasslessPredicate calls | semmle.order | 31 | -| Foo.qll:3:1:7:1 | Class Foo | Foo.qll:3:19:3:22 | TypeExpr | semmle.label | getASuperType() | -| Foo.qll:3:1:7:1 | Class Foo | Foo.qll:3:19:3:22 | TypeExpr | semmle.order | 4 | -| Foo.qll:3:1:7:1 | Class Foo | Foo.qll:4:3:4:17 | CharPred Foo | semmle.label | getCharPred() | -| Foo.qll:3:1:7:1 | Class Foo | Foo.qll:4:3:4:17 | CharPred Foo | semmle.order | 5 | -| Foo.qll:3:1:7:1 | Class Foo | Foo.qll:6:3:6:38 | ClassPredicate toString | semmle.label | getClassPredicate(_) | -| Foo.qll:3:1:7:1 | Class Foo | Foo.qll:6:3:6:38 | ClassPredicate toString | semmle.order | 9 | +| Foo.qll:1:1:27:2 | TopLevel | Foo.qll:3:7:3:9 | Class Foo | semmle.label | getAClass() | +| Foo.qll:1:1:27:2 | TopLevel | Foo.qll:3:7:3:9 | Class Foo | semmle.order | 3 | +| Foo.qll:1:1:27:2 | TopLevel | Foo.qll:9:17:9:19 | ClasslessPredicate foo | semmle.label | getAPredicate() | +| Foo.qll:1:1:27:2 | TopLevel | Foo.qll:9:17:9:19 | ClasslessPredicate foo | semmle.order | 15 | +| Foo.qll:1:1:27:2 | TopLevel | Foo.qll:13:11:13:15 | ClasslessPredicate calls | semmle.label | getAPredicate() | +| Foo.qll:1:1:27:2 | TopLevel | Foo.qll:13:11:13:15 | ClasslessPredicate calls | semmle.order | 31 | +| Foo.qll:3:7:3:9 | Class Foo | Foo.qll:3:19:3:22 | TypeExpr | semmle.label | getASuperType() | +| Foo.qll:3:7:3:9 | Class Foo | Foo.qll:3:19:3:22 | TypeExpr | semmle.order | 4 | +| Foo.qll:3:7:3:9 | Class Foo | Foo.qll:4:3:4:17 | CharPred Foo | semmle.label | getCharPred() | +| Foo.qll:3:7:3:9 | Class Foo | Foo.qll:4:3:4:17 | CharPred Foo | semmle.order | 5 | +| Foo.qll:3:7:3:9 | Class Foo | Foo.qll:6:10:6:17 | ClassPredicate toString | semmle.label | getClassPredicate(_) | +| Foo.qll:3:7:3:9 | Class Foo | Foo.qll:6:10:6:17 | ClassPredicate toString | semmle.order | 10 | | Foo.qll:4:3:4:17 | CharPred Foo | Foo.qll:4:11:4:15 | ComparisonFormula | semmle.label | getBody() | | Foo.qll:4:3:4:17 | CharPred Foo | Foo.qll:4:11:4:15 | ComparisonFormula | semmle.order | 6 | | Foo.qll:4:11:4:15 | ComparisonFormula | Foo.qll:4:11:4:11 | Integer | semmle.label | getLeftOperand() | | Foo.qll:4:11:4:15 | ComparisonFormula | Foo.qll:4:11:4:11 | Integer | semmle.order | 6 | | Foo.qll:4:11:4:15 | ComparisonFormula | Foo.qll:4:15:4:15 | Integer | semmle.label | getRightOperand() | | Foo.qll:4:11:4:15 | ComparisonFormula | Foo.qll:4:15:4:15 | Integer | semmle.order | 8 | -| Foo.qll:6:3:6:38 | ClassPredicate toString | Foo.qll:6:3:6:8 | TypeExpr | semmle.label | getReturnTypeExpr() | -| Foo.qll:6:3:6:38 | ClassPredicate toString | Foo.qll:6:3:6:8 | TypeExpr | semmle.order | 9 | -| Foo.qll:6:3:6:38 | ClassPredicate toString | Foo.qll:6:23:6:36 | ComparisonFormula | semmle.label | getBody() | -| Foo.qll:6:3:6:38 | ClassPredicate toString | Foo.qll:6:23:6:36 | ComparisonFormula | semmle.order | 11 | +| Foo.qll:6:10:6:17 | ClassPredicate toString | Foo.qll:6:3:6:8 | TypeExpr | semmle.label | getReturnTypeExpr() | +| Foo.qll:6:10:6:17 | ClassPredicate toString | Foo.qll:6:3:6:8 | TypeExpr | semmle.order | 9 | +| Foo.qll:6:10:6:17 | ClassPredicate toString | Foo.qll:6:23:6:36 | ComparisonFormula | semmle.label | getBody() | +| Foo.qll:6:10:6:17 | ClassPredicate toString | Foo.qll:6:23:6:36 | ComparisonFormula | semmle.order | 11 | | Foo.qll:6:23:6:36 | ComparisonFormula | Foo.qll:6:23:6:28 | result | semmle.label | getLeftOperand() | | Foo.qll:6:23:6:36 | ComparisonFormula | Foo.qll:6:23:6:28 | result | semmle.order | 11 | | Foo.qll:6:23:6:36 | ComparisonFormula | Foo.qll:6:32:6:36 | String | semmle.label | getRightOperand() | | Foo.qll:6:23:6:36 | ComparisonFormula | Foo.qll:6:32:6:36 | String | semmle.order | 13 | -| Foo.qll:9:7:11:1 | ClasslessPredicate foo | Foo.qll:9:1:9:5 | annotation | semmle.label | getAnAnnotation() | -| Foo.qll:9:7:11:1 | ClasslessPredicate foo | Foo.qll:9:1:9:5 | annotation | semmle.order | 14 | -| Foo.qll:9:7:11:1 | ClasslessPredicate foo | Foo.qll:9:21:9:25 | f | semmle.label | getParameter(_) | -| Foo.qll:9:7:11:1 | ClasslessPredicate foo | Foo.qll:9:21:9:25 | f | semmle.order | 16 | -| Foo.qll:9:7:11:1 | ClasslessPredicate foo | Foo.qll:10:3:10:85 | ComparisonFormula | semmle.label | getBody() | -| Foo.qll:9:7:11:1 | ClasslessPredicate foo | Foo.qll:10:3:10:85 | ComparisonFormula | semmle.order | 18 | +| Foo.qll:9:17:9:19 | ClasslessPredicate foo | Foo.qll:9:1:9:5 | annotation | semmle.label | getAnAnnotation() | +| Foo.qll:9:17:9:19 | ClasslessPredicate foo | Foo.qll:9:1:9:5 | annotation | semmle.order | 14 | +| Foo.qll:9:17:9:19 | ClasslessPredicate foo | Foo.qll:9:21:9:25 | f | semmle.label | getParameter(_) | +| Foo.qll:9:17:9:19 | ClasslessPredicate foo | Foo.qll:9:21:9:25 | f | semmle.order | 16 | +| Foo.qll:9:17:9:19 | ClasslessPredicate foo | Foo.qll:10:3:10:85 | ComparisonFormula | semmle.label | getBody() | +| Foo.qll:9:17:9:19 | ClasslessPredicate foo | Foo.qll:10:3:10:85 | ComparisonFormula | semmle.order | 18 | | Foo.qll:9:21:9:25 | f | Foo.qll:9:21:9:23 | TypeExpr | semmle.label | getTypeExpr() | | Foo.qll:9:21:9:25 | f | Foo.qll:9:21:9:23 | TypeExpr | semmle.order | 16 | | Foo.qll:10:3:10:85 | ComparisonFormula | Foo.qll:10:3:10:3 | f | semmle.label | getLeftOperand() | @@ -297,10 +297,10 @@ edges | Foo.qll:10:27:10:50 | ComparisonFormula | Foo.qll:10:46:10:50 | String | semmle.order | 27 | | Foo.qll:10:69:10:84 | MemberCall | Foo.qll:10:69:10:73 | inner | semmle.label | getBase() | | Foo.qll:10:69:10:84 | MemberCall | Foo.qll:10:69:10:73 | inner | semmle.order | 29 | -| Foo.qll:13:1:27:1 | ClasslessPredicate calls | Foo.qll:13:17:13:21 | f | semmle.label | getParameter(_) | -| Foo.qll:13:1:27:1 | ClasslessPredicate calls | Foo.qll:13:17:13:21 | f | semmle.order | 32 | -| Foo.qll:13:1:27:1 | ClasslessPredicate calls | Foo.qll:14:3:26:14 | Disjunction | semmle.label | getBody() | -| Foo.qll:13:1:27:1 | ClasslessPredicate calls | Foo.qll:14:3:26:14 | Disjunction | semmle.order | 34 | +| Foo.qll:13:11:13:15 | ClasslessPredicate calls | Foo.qll:13:17:13:21 | f | semmle.label | getParameter(_) | +| Foo.qll:13:11:13:15 | ClasslessPredicate calls | Foo.qll:13:17:13:21 | f | semmle.order | 32 | +| Foo.qll:13:11:13:15 | ClasslessPredicate calls | Foo.qll:14:3:26:14 | Disjunction | semmle.label | getBody() | +| Foo.qll:13:11:13:15 | ClasslessPredicate calls | Foo.qll:14:3:26:14 | Disjunction | semmle.order | 34 | | Foo.qll:13:17:13:21 | f | Foo.qll:13:17:13:19 | TypeExpr | semmle.label | getTypeExpr() | | Foo.qll:13:17:13:21 | f | Foo.qll:13:17:13:19 | TypeExpr | semmle.order | 32 | | Foo.qll:14:3:14:10 | PredicateCall | Foo.qll:14:9:14:9 | f | semmle.label | getArgument(_) | diff --git a/ql/ql/test/queries/style/AcronymsShouldBeCamelCase/AcronymsShouldBeCamelCase.expected b/ql/ql/test/queries/style/AcronymsShouldBeCamelCase/AcronymsShouldBeCamelCase.expected index bd6bc99489d..de23b92bafa 100644 --- a/ql/ql/test/queries/style/AcronymsShouldBeCamelCase/AcronymsShouldBeCamelCase.expected +++ b/ql/ql/test/queries/style/AcronymsShouldBeCamelCase/AcronymsShouldBeCamelCase.expected @@ -1,3 +1,3 @@ -| Test.qll:2:1:2:27 | ClasslessPredicate isXML | Acronyms in isXML should be PascalCase/camelCase | -| Test.qll:8:1:10:15 | NewType TXMLElements | Acronyms in TXMLElements should be PascalCase/camelCase | -| Test.qll:10:3:10:15 | NewTypeBranch TXMLElement | Acronyms in TXMLElement should be PascalCase/camelCase | +| Test.qll:2:11:2:15 | ClasslessPredicate isXML | Acronyms in isXML should be PascalCase/camelCase | +| Test.qll:8:9:8:20 | NewType TXMLElements | Acronyms in TXMLElements should be PascalCase/camelCase | +| Test.qll:10:3:10:13 | NewTypeBranch TXMLElement | Acronyms in TXMLElement should be PascalCase/camelCase | diff --git a/ql/ql/test/queries/style/DeadCode/DeadCode.expected b/ql/ql/test/queries/style/DeadCode/DeadCode.expected index d3b3115e729..39a2b034390 100644 --- a/ql/ql/test/queries/style/DeadCode/DeadCode.expected +++ b/ql/ql/test/queries/style/DeadCode/DeadCode.expected @@ -1,2 +1,2 @@ -| Foo.qll:2:11:2:38 | ClasslessPredicate dead1 | Code is dead | -| Foo.qll:6:3:6:30 | ClasslessPredicate dead2 | Code is dead | +| Foo.qll:2:21:2:25 | ClasslessPredicate dead1 | Code is dead | +| Foo.qll:6:13:6:17 | ClasslessPredicate dead2 | Code is dead | diff --git a/ql/ql/test/queries/style/MissingOverride/MissingOverride.expected b/ql/ql/test/queries/style/MissingOverride/MissingOverride.expected index f59b21d0d73..2ef0dd4b1ad 100644 --- a/ql/ql/test/queries/style/MissingOverride/MissingOverride.expected +++ b/ql/ql/test/queries/style/MissingOverride/MissingOverride.expected @@ -1 +1 @@ -| Test.qll:12:3:12:33 | ClassPredicate test | Wrong.test overrides $@ but does not have an override annotation. | Test.qll:4:3:4:40 | ClassPredicate test | Super.test | +| Test.qll:12:13:12:16 | ClassPredicate test | Wrong.test overrides $@ but does not have an override annotation. | Test.qll:4:13:4:16 | ClassPredicate test | Super.test | diff --git a/ql/ql/test/queries/style/Misspelling/Misspelling.expected b/ql/ql/test/queries/style/Misspelling/Misspelling.expected index dc028c412d8..65f9f245d3b 100644 --- a/ql/ql/test/queries/style/Misspelling/Misspelling.expected +++ b/ql/ql/test/queries/style/Misspelling/Misspelling.expected @@ -1,6 +1,6 @@ | Test.qll:1:1:3:3 | QLDoc | This QLDoc comment contains the common misspelling 'mispelled', which should instead be 'misspelled'. | -| Test.qll:4:1:11:1 | Class PublicallyAccessible | This class name contains the common misspelling 'publically', which should instead be 'publicly'. | +| Test.qll:4:7:4:26 | Class PublicallyAccessible | This class name contains the common misspelling 'publically', which should instead be 'publicly'. | | Test.qll:5:3:5:20 | FieldDecl | This field name contains the common misspelling 'occurences', which should instead be 'occurrences'. | -| Test.qll:10:3:10:36 | ClassPredicate hasAgrument | This classPredicate name contains the common misspelling 'agrument', which should instead be 'argument'. | +| Test.qll:10:13:10:23 | ClassPredicate hasAgrument | This classPredicate name contains the common misspelling 'agrument', which should instead be 'argument'. | | Test.qll:13:1:16:3 | QLDoc | This QLDoc comment contains the non-US spelling 'colour', which should instead be 'color'. | -| Test.qll:17:1:22:1 | Class AnalysedInt | This class name contains the non-US spelling 'analysed', which should instead be 'analyzed'. | +| Test.qll:17:7:17:17 | Class AnalysedInt | This class name contains the non-US spelling 'analysed', which should instead be 'analyzed'. | diff --git a/ql/ql/test/type/type.expected b/ql/ql/test/type/type.expected index 773a9244a22..fd3a34c27f6 100644 --- a/ql/ql/test/type/type.expected +++ b/ql/ql/test/type/type.expected @@ -1,6 +1,6 @@ -| Test.qll:4:15:4:18 | this | Test.qll:3:1:5:1 | Strings | -| Test.qll:4:15:4:18 | this | Test.qll:3:1:5:1 | Strings.Strings | -| Test.qll:4:15:4:18 | this | Test.qll:3:1:5:1 | Strings.extends | +| Test.qll:4:15:4:18 | this | Test.qll:3:7:3:13 | Strings | +| Test.qll:4:15:4:18 | this | Test.qll:3:7:3:13 | Strings.Strings | +| Test.qll:4:15:4:18 | this | Test.qll:3:7:3:13 | Strings.extends | | Test.qll:4:22:4:76 | Set | file://:0:0:0:0 | string | | Test.qll:4:23:4:24 | String | file://:0:0:0:0 | string | | Test.qll:4:27:4:29 | String | file://:0:0:0:0 | string | @@ -12,9 +12,9 @@ | Test.qll:4:61:4:63 | String | file://:0:0:0:0 | string | | Test.qll:4:66:4:69 | String | file://:0:0:0:0 | string | | Test.qll:4:72:4:75 | String | file://:0:0:0:0 | string | -| Test.qll:8:14:8:17 | this | Test.qll:7:1:9:1 | Floats | -| Test.qll:8:14:8:17 | this | Test.qll:7:1:9:1 | Floats.Floats | -| Test.qll:8:14:8:17 | this | Test.qll:7:1:9:1 | Floats.extends | +| Test.qll:8:14:8:17 | this | Test.qll:7:7:7:12 | Floats | +| Test.qll:8:14:8:17 | this | Test.qll:7:7:7:12 | Floats.Floats | +| Test.qll:8:14:8:17 | this | Test.qll:7:7:7:12 | Floats.extends | | Test.qll:8:21:8:70 | Set | file://:0:0:0:0 | float | | Test.qll:8:22:8:24 | Float | file://:0:0:0:0 | float | | Test.qll:8:27:8:29 | Float | file://:0:0:0:0 | float | @@ -27,28 +27,28 @@ | Test.qll:8:62:8:64 | Float | file://:0:0:0:0 | float | | Test.qll:8:67:8:69 | Float | file://:0:0:0:0 | float | | Test.qll:11:37:11:42 | result | file://:0:0:0:0 | string | -| Test.qll:11:46:11:46 | a | Test.qll:3:1:5:1 | Strings | +| Test.qll:11:46:11:46 | a | Test.qll:3:7:3:13 | Strings | | Test.qll:11:46:11:50 | AddExpr | file://:0:0:0:0 | string | -| Test.qll:11:50:11:50 | b | Test.qll:3:1:5:1 | Strings | +| Test.qll:11:50:11:50 | b | Test.qll:3:7:3:13 | Strings | | Test.qll:13:36:13:41 | result | file://:0:0:0:0 | float | -| Test.qll:13:45:13:45 | a | Test.qll:7:1:9:1 | Floats | +| Test.qll:13:45:13:45 | a | Test.qll:7:7:7:12 | Floats | | Test.qll:13:45:13:49 | AddExpr | file://:0:0:0:0 | float | -| Test.qll:13:49:13:49 | b | Test.qll:7:1:9:1 | Floats | -| Test.qll:16:12:16:15 | this | Test.qll:15:1:19:1 | Base | -| Test.qll:16:12:16:15 | this | Test.qll:15:1:19:1 | Base.Base | -| Test.qll:16:12:16:15 | this | Test.qll:15:1:19:1 | Base.extends | +| Test.qll:13:49:13:49 | b | Test.qll:7:7:7:12 | Floats | +| Test.qll:16:12:16:15 | this | Test.qll:15:7:15:10 | Base | +| Test.qll:16:12:16:15 | this | Test.qll:15:7:15:10 | Base.Base | +| Test.qll:16:12:16:15 | this | Test.qll:15:7:15:10 | Base.extends | | Test.qll:16:19:16:23 | String | file://:0:0:0:0 | string | | Test.qll:18:15:18:20 | result | file://:0:0:0:0 | int | | Test.qll:18:24:18:24 | Integer | file://:0:0:0:0 | int | -| Test.qll:22:11:22:14 | this | Test.qll:21:1:27:1 | Sub | -| Test.qll:22:11:22:14 | this | Test.qll:21:1:27:1 | Sub.Sub | -| Test.qll:22:11:22:14 | this | Test.qll:21:1:27:1 | Sub.extends | +| Test.qll:22:11:22:14 | this | Test.qll:21:7:21:9 | Sub | +| Test.qll:22:11:22:14 | this | Test.qll:21:7:21:9 | Sub.Sub | +| Test.qll:22:11:22:14 | this | Test.qll:21:7:21:9 | Sub.extends | | Test.qll:22:18:22:22 | String | file://:0:0:0:0 | string | | Test.qll:24:15:24:20 | result | file://:0:0:0:0 | int | -| Test.qll:24:24:24:33 | Super | Test.qll:15:1:19:1 | Base | +| Test.qll:24:24:24:33 | Super | Test.qll:15:7:15:10 | Base | | Test.qll:24:24:24:39 | MemberCall | file://:0:0:0:0 | int | | Test.qll:26:16:26:21 | result | file://:0:0:0:0 | int | -| Test.qll:26:25:26:29 | Super | Test.qll:15:1:19:1 | Base | +| Test.qll:26:25:26:29 | Super | Test.qll:15:7:15:10 | Base | | Test.qll:26:25:26:35 | MemberCall | file://:0:0:0:0 | int | | Test.qll:30:32:30:37 | result | file://:0:0:0:0 | int | | Test.qll:30:41:30:41 | a | file://:0:0:0:0 | int | From 1ec838e671f1eeeb81ad1abfa3124f1bcdb2b863 Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Tue, 21 Jun 2022 09:14:23 -0700 Subject: [PATCH 072/505] Update docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst Co-authored-by: James Fletcher <42464962+jf205@users.noreply.github.com> --- .../codeql-cli/analyzing-databases-with-the-codeql-cli.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst b/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst index 84b76615520..6c7169634bb 100644 --- a/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst +++ b/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst @@ -174,6 +174,7 @@ To analyze your database using the `cpp-security-and-quality.qls` query suite fr codeql database analyze --format=sarif-latest --output=results \ 'codeql/cpp-queries@~0.0.3:codeql-suites/cpp-security-and-quality.qls' + For more information about CodeQL packs, see :doc:`About CodeQL Packs `. Running query suites From 83b720d730c90f932b8fb1a2862645045233c804 Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Sat, 18 Jun 2022 20:43:58 +0000 Subject: [PATCH 073/505] first draft of weak params query --- .../experimental/weak-params/WeakParams.ql | 46 +++++++++++++++++++ .../security/weak-params/WeakParams.expected | 0 .../security/weak-params/WeakParams.qlref | 1 + .../security/weak-params/WeakParams.rb | 15 ++++++ 4 files changed, 62 insertions(+) create mode 100644 ruby/ql/src/experimental/weak-params/WeakParams.ql create mode 100644 ruby/ql/test/query-tests/security/weak-params/WeakParams.expected create mode 100644 ruby/ql/test/query-tests/security/weak-params/WeakParams.qlref create mode 100644 ruby/ql/test/query-tests/security/weak-params/WeakParams.rb diff --git a/ruby/ql/src/experimental/weak-params/WeakParams.ql b/ruby/ql/src/experimental/weak-params/WeakParams.ql new file mode 100644 index 00000000000..b756d6ed8b8 --- /dev/null +++ b/ruby/ql/src/experimental/weak-params/WeakParams.ql @@ -0,0 +1,46 @@ +/** + * @name Weak or direct parameter references are used + * @description Directly checking request parameters without following a strong params pattern can lead to unintentional avenues for injection attacks. + * @kind problem + * @problem.severity error + * @security-severity 5.0 + * @precision low + * @id rb/weak-params + * @tags security + */ + +import ruby + +class WeakParams extends AstNode { + WeakParams() { + this instanceof UnspecificParamsMethod or + this instanceof ParamsReference + } +} + +class StrongParamsMethod extends Method { + StrongParamsMethod() { this.getName().regexpMatch(".*_params") } +} + +class UnspecificParamsMethod extends MethodCall { + UnspecificParamsMethod() { + ( + this.getMethodName() = "expose_all" or + this.getMethodName() = "original_hash" or + this.getMethodName() = "path_parametes" or + this.getMethodName() = "query_parameters" or + this.getMethodName() = "request_parameters" or + this.getMethodName() = "GET" or + this.getMethodName() = "POST" + ) + } +} + +class ParamsReference extends ElementReference { + ParamsReference() { this.getAChild().toString() = "params" } +} + +from WeakParams params +where not params.getEnclosingMethod() instanceof StrongParamsMethod +select params, + "By exposing all keys in request parameters or by blindy accessing them, unintended parameters could be used and lead to mass-assignment or have other unexpected side-effects." diff --git a/ruby/ql/test/query-tests/security/weak-params/WeakParams.expected b/ruby/ql/test/query-tests/security/weak-params/WeakParams.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/ruby/ql/test/query-tests/security/weak-params/WeakParams.qlref b/ruby/ql/test/query-tests/security/weak-params/WeakParams.qlref new file mode 100644 index 00000000000..5350e4bf40a --- /dev/null +++ b/ruby/ql/test/query-tests/security/weak-params/WeakParams.qlref @@ -0,0 +1 @@ +experimental/weak-params/WeakParams.ql \ No newline at end of file diff --git a/ruby/ql/test/query-tests/security/weak-params/WeakParams.rb b/ruby/ql/test/query-tests/security/weak-params/WeakParams.rb new file mode 100644 index 00000000000..cc0dc80341f --- /dev/null +++ b/ruby/ql/test/query-tests/security/weak-params/WeakParams.rb @@ -0,0 +1,15 @@ +class TestController < ActionController::Base + def create + TestObject.new(request.request_parameters) + end + + def create_query + TestObject.new(request.query_parameters) + end + + # + def object_params + p = params.query_parameters + params.require(:uuid).permit(:notes) + end +end \ No newline at end of file From 53729f99c5b0e5eb08903bd27797446b2f7ec23f Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Tue, 21 Jun 2022 20:28:29 +0000 Subject: [PATCH 074/505] restrict findings to just controller classes --- ruby/ql/src/experimental/weak-params/WeakParams.ql | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ruby/ql/src/experimental/weak-params/WeakParams.ql b/ruby/ql/src/experimental/weak-params/WeakParams.ql index b756d6ed8b8..d7408f8bea7 100644 --- a/ruby/ql/src/experimental/weak-params/WeakParams.ql +++ b/ruby/ql/src/experimental/weak-params/WeakParams.ql @@ -18,6 +18,10 @@ class WeakParams extends AstNode { } } +class ControllerClass extends ModuleBase { + ControllerClass() { this.getModule().getSuperClass+().toString() = "ApplicationController" } +} + class StrongParamsMethod extends Method { StrongParamsMethod() { this.getName().regexpMatch(".*_params") } } @@ -41,6 +45,8 @@ class ParamsReference extends ElementReference { } from WeakParams params -where not params.getEnclosingMethod() instanceof StrongParamsMethod +where + not params.getEnclosingMethod() instanceof StrongParamsMethod and + params.getEnclosingModule() instanceof ControllerClass select params, "By exposing all keys in request parameters or by blindy accessing them, unintended parameters could be used and lead to mass-assignment or have other unexpected side-effects." From 990747cd229b83f467c018d50ebd0ffdea93607d Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Tue, 21 Jun 2022 21:27:18 +0000 Subject: [PATCH 075/505] Limit findings to just those called in Controllers --- .../manually-check-http-verb/ManuallyCheckHttpVerb.ql | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql b/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql index 6946d26a7e8..98c2c266cab 100644 --- a/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql +++ b/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql @@ -23,6 +23,10 @@ class HttpVerbMethod extends MethodCall { } } +class ControllerClass extends ModuleBase { + ControllerClass() { this.getModule().getSuperClass+().toString() = "ApplicationController" } +} + class CheckRequestMethodFromEnv extends DataFlow::CallNode { CheckRequestMethodFromEnv() { // is this node an instance of `env["REQUEST_METHOD"] @@ -78,7 +82,10 @@ class CheckHeadRequest extends MethodCall { from CheckRequestMethodFromEnv env, AstNode node where - node instanceof HttpVerbMethod or - node = env.asExpr().getExpr() + ( + node instanceof HttpVerbMethod or + node = env.asExpr().getExpr() + ) and + node.getEnclosingModule() instanceof ControllerClass select node, "Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods." From c767f241ad919d67b7a718207f669a457b464d53 Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Wed, 22 Jun 2022 02:12:23 +0000 Subject: [PATCH 076/505] narrow query scope --- .../ManuallyCheckHttpVerb.ql | 65 +++++-------------- 1 file changed, 16 insertions(+), 49 deletions(-) diff --git a/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql b/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql index 98c2c266cab..6e48255d4d6 100644 --- a/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql +++ b/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql @@ -12,39 +12,30 @@ import ruby import codeql.ruby.DataFlow -class HttpVerbMethod extends MethodCall { - HttpVerbMethod() { - this instanceof CheckGetRequest or - this instanceof CheckPostRequest or - this instanceof CheckPatchRequest or - this instanceof CheckPutRequest or - this instanceof CheckDeleteRequest or - this instanceof CheckHeadRequest - } +class CheckNotGetRequest extends ConditionalExpr { + CheckNotGetRequest() { this.getCondition() instanceof CheckGetRequest } +} + +class CheckGetRequest extends MethodCall { + CheckGetRequest() { this.getMethodName() = "get?" } } class ControllerClass extends ModuleBase { ControllerClass() { this.getModule().getSuperClass+().toString() = "ApplicationController" } } -class CheckRequestMethodFromEnv extends DataFlow::CallNode { - CheckRequestMethodFromEnv() { +class CheckGetFromEnv extends AstNode { + CheckGetFromEnv() { // is this node an instance of `env["REQUEST_METHOD"] - this.getExprNode().getNode() instanceof GetRequestMethodFromEnv and + this instanceof GetRequestMethodFromEnv and ( // and is this node a param of a call to `.include?` - exists(MethodCall call | call.getAnArgument() = this.getExprNode().getNode() | - call.getMethodName() = "include?" - ) + exists(MethodCall call | call.getAnArgument() = this | call.getMethodName() = "include?") or - exists(DataFlow::Node node | - node.asExpr().getExpr().(MethodCall).getMethodName() = "include?" - | - node.getALocalSource() = this + // check if env["REQUEST_METHOD"] is compared to GET + exists(EqualityOperation eq | eq.getAChild() = this | + eq.getAChild().(StringLiteral).toString() = "GET" ) - or - // or is this node on either size of an equality comparison - exists(EqualityOperation eq | eq.getAChild() = this.getExprNode().getNode()) ) } } @@ -56,35 +47,11 @@ class GetRequestMethodFromEnv extends ElementReference { } } -class CheckGetRequest extends MethodCall { - CheckGetRequest() { this.getMethodName() = "get?" } -} - -class CheckPostRequest extends MethodCall { - CheckPostRequest() { this.getMethodName() = "post?" } -} - -class CheckPutRequest extends MethodCall { - CheckPutRequest() { this.getMethodName() = "put?" } -} - -class CheckPatchRequest extends MethodCall { - CheckPatchRequest() { this.getMethodName() = "patch?" } -} - -class CheckDeleteRequest extends MethodCall { - CheckDeleteRequest() { this.getMethodName() = "delete?" } -} - -class CheckHeadRequest extends MethodCall { - CheckHeadRequest() { this.getMethodName() = "head?" } -} - -from CheckRequestMethodFromEnv env, AstNode node +from AstNode node where ( - node instanceof HttpVerbMethod or - node = env.asExpr().getExpr() + node instanceof CheckNotGetRequest or + node instanceof CheckGetFromEnv ) and node.getEnclosingModule() instanceof ControllerClass select node, From 995f36556847cb5997750f5d3d92600764b26d93 Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Wed, 22 Jun 2022 02:17:01 +0000 Subject: [PATCH 077/505] just check string literal --- .../manually-check-http-verb/ManuallyCheckHttpVerb.ql | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql b/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql index 6e48255d4d6..a006587a13e 100644 --- a/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql +++ b/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql @@ -28,14 +28,9 @@ class CheckGetFromEnv extends AstNode { CheckGetFromEnv() { // is this node an instance of `env["REQUEST_METHOD"] this instanceof GetRequestMethodFromEnv and - ( - // and is this node a param of a call to `.include?` - exists(MethodCall call | call.getAnArgument() = this | call.getMethodName() = "include?") - or - // check if env["REQUEST_METHOD"] is compared to GET - exists(EqualityOperation eq | eq.getAChild() = this | - eq.getAChild().(StringLiteral).toString() = "GET" - ) + // check if env["REQUEST_METHOD"] is compared to GET + exists(EqualityOperation eq | eq.getAChild() = this | + eq.getAChild().(StringLiteral).toString() = "GET" ) } } From 0ef97b41c8fab10f56581f40f6f325cbcdae4594 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Wed, 22 Jun 2022 13:03:10 +0200 Subject: [PATCH 078/505] C#: Update .NET Runtime models and add sources and sinks. --- .../frameworks/generated/dotnet/Runtime.qll | 256 +----------------- 1 file changed, 13 insertions(+), 243 deletions(-) diff --git a/csharp/ql/lib/semmle/code/csharp/frameworks/generated/dotnet/Runtime.qll b/csharp/ql/lib/semmle/code/csharp/frameworks/generated/dotnet/Runtime.qll index b3f247ede94..f4c5596c973 100644 --- a/csharp/ql/lib/semmle/code/csharp/frameworks/generated/dotnet/Runtime.qll +++ b/csharp/ql/lib/semmle/code/csharp/frameworks/generated/dotnet/Runtime.qll @@ -1,11 +1,23 @@ /** * THIS FILE IS AN AUTO-GENERATED MODELS AS DATA FILE. DO NOT EDIT. - * Definitions of taint steps in the dotnetruntime framework. + * Definitions of taint steps in the Runtime framework. */ import csharp private import semmle.code.csharp.dataflow.ExternalFlow +private class RuntimeSinksCsv extends SinkModelCsv { + override predicate row(string row) { + row = + [ + "System.Data.Odbc;OdbcDataAdapter;false;OdbcDataAdapter;(System.String,System.Data.Odbc.OdbcConnection);;Argument[0];sql;generated", + "System.Data.Odbc;OdbcDataAdapter;false;OdbcDataAdapter;(System.String,System.String);;Argument[0];sql;generated", + "System.Net.Http;StringContent;false;StringContent;(System.String);;Argument[0];xss;generated", + "System.Net.Http;StringContent;false;StringContent;(System.String,System.Text.Encoding);;Argument[0];xss;generated" + ] + } +} + private class RuntimeSummaryCsv extends SummaryModelCsv { override predicate row(string row) { row = @@ -529,11 +541,8 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.CodeDom.Compiler;GeneratedCodeAttribute;false;GeneratedCodeAttribute;(System.String,System.String);;Argument[1];Argument[Qualifier];taint;generated", "System.CodeDom.Compiler;GeneratedCodeAttribute;false;get_Tool;();;Argument[Qualifier];ReturnValue;taint;generated", "System.CodeDom.Compiler;GeneratedCodeAttribute;false;get_Version;();;Argument[Qualifier];ReturnValue;taint;generated", - "System.CodeDom.Compiler;IndentedTextWriter;false;FlushAsync;();;Argument[Qualifier];ReturnValue;taint;generated", "System.CodeDom.Compiler;IndentedTextWriter;false;IndentedTextWriter;(System.IO.TextWriter,System.String);;Argument[0];Argument[Qualifier];taint;generated", "System.CodeDom.Compiler;IndentedTextWriter;false;IndentedTextWriter;(System.IO.TextWriter,System.String);;Argument[1];Argument[Qualifier];taint;generated", - "System.CodeDom.Compiler;IndentedTextWriter;false;WriteLineNoTabsAsync;(System.String);;Argument[0];ReturnValue;taint;generated", - "System.CodeDom.Compiler;IndentedTextWriter;false;WriteLineNoTabsAsync;(System.String);;Argument[Qualifier];ReturnValue;taint;generated", "System.CodeDom.Compiler;IndentedTextWriter;false;get_Encoding;();;Argument[Qualifier];ReturnValue;taint;generated", "System.CodeDom.Compiler;IndentedTextWriter;false;get_InnerWriter;();;Argument[Qualifier];ReturnValue;taint;generated", "System.CodeDom.Compiler;IndentedTextWriter;false;get_NewLine;();;Argument[Qualifier];ReturnValue;taint;generated", @@ -746,7 +755,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.CodeDom;CodeNamespaceImport;false;set_Namespace;(System.String);;Argument[0];Argument[Qualifier];taint;generated", "System.CodeDom;CodeNamespaceImportCollection;false;Add;(System.CodeDom.CodeNamespaceImport);;Argument[0];Argument[Qualifier];taint;generated", "System.CodeDom;CodeNamespaceImportCollection;false;AddRange;(System.CodeDom.CodeNamespaceImport[]);;Argument[0].Element;Argument[Qualifier];taint;generated", - "System.CodeDom;CodeNamespaceImportCollection;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated", "System.CodeDom;CodeNamespaceImportCollection;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", "System.CodeDom;CodeNamespaceImportCollection;false;set_Item;(System.Int32,System.CodeDom.CodeNamespaceImport);;Argument[1];Argument[Qualifier];taint;generated", "System.CodeDom;CodeObject;false;get_UserData;();;Argument[Qualifier];ReturnValue;taint;generated", @@ -919,7 +927,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Collections.Concurrent;ConcurrentBag<>;false;TryAdd;(T);;Argument[0];Argument[Qualifier];taint;generated", "System.Collections.Concurrent;ConcurrentBag<>;false;TryPeek;(T);;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Concurrent;ConcurrentBag<>;false;TryTake;(T);;Argument[Qualifier];ReturnValue;taint;generated", - "System.Collections.Concurrent;ConcurrentDictionary<,>;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Concurrent;ConcurrentDictionary<,>;false;GetOrAdd;(TKey,TValue);;Argument[1];ReturnValue;taint;generated", "System.Collections.Concurrent;ConcurrentDictionary<,>;false;get_Comparer;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Concurrent;ConcurrentStack<>;false;ConcurrentStack;(System.Collections.Generic.IEnumerable);;Argument[0].Element;Argument[Qualifier];taint;generated", @@ -933,7 +940,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Collections.Concurrent;Partitioner;false;Create<>;(System.Collections.Generic.IEnumerable,System.Collections.Concurrent.EnumerablePartitionerOptions);;Argument[0].Element;ReturnValue;taint;generated", "System.Collections.Concurrent;Partitioner;false;Create<>;(System.Collections.Generic.IList,System.Boolean);;Argument[0].Element;ReturnValue;taint;generated", "System.Collections.Concurrent;Partitioner;false;Create<>;(TSource[],System.Boolean);;Argument[0].Element;ReturnValue;taint;generated", - "System.Collections.Generic;CollectionExtensions;false;AsReadOnly<,>;(System.Collections.Generic.IDictionary);;Argument[0].Element;ReturnValue;taint;generated", "System.Collections.Generic;CollectionExtensions;false;AsReadOnly<>;(System.Collections.Generic.IList);;Argument[0].Element;ReturnValue;taint;generated", "System.Collections.Generic;CollectionExtensions;false;GetDefaultAssets;(System.Collections.Generic.IEnumerable);;Argument[0].Element;ReturnValue;taint;generated", "System.Collections.Generic;CollectionExtensions;false;GetDefaultGroup;(System.Collections.Generic.IEnumerable);;Argument[0].Element;ReturnValue;taint;generated", @@ -945,9 +951,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Collections.Generic;CollectionExtensions;false;GetValueOrDefault<,>;(System.Collections.Generic.IReadOnlyDictionary,TKey,TValue);;Argument[1];ReturnValue;taint;generated", "System.Collections.Generic;CollectionExtensions;false;GetValueOrDefault<,>;(System.Collections.Generic.IReadOnlyDictionary,TKey,TValue);;Argument[2];ReturnValue;taint;generated", "System.Collections.Generic;CollectionExtensions;false;Remove<,>;(System.Collections.Generic.IDictionary,TKey,TValue);;Argument[0].Element;ReturnValue;taint;generated", - "System.Collections.Generic;CollectionExtensions;false;TryAdd<,>;(System.Collections.Generic.IDictionary,TKey,TValue);;Argument[0].Element;Argument[2];taint;generated", - "System.Collections.Generic;CollectionExtensions;false;TryAdd<,>;(System.Collections.Generic.IDictionary,TKey,TValue);;Argument[1];Argument[0].Element;taint;generated", - "System.Collections.Generic;CollectionExtensions;false;TryAdd<,>;(System.Collections.Generic.IDictionary,TKey,TValue);;Argument[2];Argument[0].Element;taint;generated", "System.Collections.Generic;Dictionary<,>+Enumerator;false;get_Current;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Generic;Dictionary<,>+Enumerator;false;get_Entry;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Generic;Dictionary<,>+Enumerator;false;get_Key;();;Argument[Qualifier];ReturnValue;taint;generated", @@ -967,8 +970,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Collections.Generic;HashSet<>;false;HashSet;(System.Collections.Generic.IEqualityComparer);;Argument[0];Argument[Qualifier];taint;generated", "System.Collections.Generic;HashSet<>;false;TryGetValue;(T,T);;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Generic;HashSet<>;false;get_Comparer;();;Argument[Qualifier];ReturnValue;taint;generated", - "System.Collections.Generic;KeyValuePair;false;Create<,>;(TKey,TValue);;Argument[0];ReturnValue;taint;generated", - "System.Collections.Generic;KeyValuePair;false;Create<,>;(TKey,TValue);;Argument[1];ReturnValue;taint;generated", "System.Collections.Generic;KeyValuePair<,>;false;Deconstruct;(TKey,TValue);;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Generic;KeyValuePair<,>;false;get_Key;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Generic;KeyValuePair<,>;false;get_Value;();;Argument[Qualifier];ReturnValue;taint;generated", @@ -1120,12 +1121,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Collections.Immutable;ImmutableDictionary;false;Create<,>;(System.Collections.Generic.IEqualityComparer);;Argument[0];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableDictionary;false;Create<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEqualityComparer);;Argument[0];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableDictionary;false;Create<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEqualityComparer);;Argument[1];ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEnumerable>);;Argument[0].Element;ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[0];ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[1].Element;ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[0];ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[1];ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[2].Element;ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableDictionary;false;GetValueOrDefault<,>;(System.Collections.Immutable.IImmutableDictionary,TKey,TValue);;Argument[2];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableDictionary;false;ToImmutableDictionary<,>;(System.Collections.Generic.IEnumerable>);;Argument[0].Element;ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableDictionary;false;ToImmutableDictionary<,>;(System.Collections.Generic.IEnumerable>,System.Collections.Generic.IEqualityComparer);;Argument[0].Element;ReturnValue;taint;generated", @@ -1142,7 +1137,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Collections.Immutable;ImmutableDictionary<,>+Builder;false;get_ValueComparer;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableDictionary<,>+Builder;false;set_KeyComparer;(System.Collections.Generic.IEqualityComparer);;Argument[0];Argument[Qualifier];taint;generated", "System.Collections.Immutable;ImmutableDictionary<,>+Builder;false;set_ValueComparer;(System.Collections.Generic.IEqualityComparer);;Argument[0];Argument[Qualifier];taint;generated", - "System.Collections.Immutable;ImmutableDictionary<,>;false;Clear;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableDictionary<,>;false;Remove;(TKey);;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableDictionary<,>;false;RemoveRange;(System.Collections.Generic.IEnumerable);;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableDictionary<,>;false;SetItem;(TKey,TValue);;Argument[Qualifier];ReturnValue;taint;generated", @@ -1155,8 +1149,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Collections.Immutable;ImmutableDictionary<,>;false;get_KeyComparer;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableDictionary<,>;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated", "System.Collections.Immutable;ImmutableDictionary<,>;false;get_ValueComparer;();;Argument[Qualifier];ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableHashSet;false;Create<>;(System.Collections.Generic.IEqualityComparer,T);;Argument[1];ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableHashSet;false;Create<>;(T);;Argument[0];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableHashSet;false;CreateRange<>;(System.Collections.Generic.IEnumerable);;Argument[0].Element;ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableHashSet;false;CreateRange<>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable);;Argument[1].Element;ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableHashSet;false;ToImmutableHashSet<>;(System.Collections.Generic.IEnumerable);;Argument[0].Element;ReturnValue;taint;generated", @@ -1166,7 +1158,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Collections.Immutable;ImmutableHashSet<>+Builder;false;TryGetValue;(T,T);;Argument[0];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableHashSet<>+Builder;false;get_KeyComparer;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableHashSet<>+Builder;false;set_KeyComparer;(System.Collections.Generic.IEqualityComparer);;Argument[0];Argument[Qualifier];taint;generated", - "System.Collections.Immutable;ImmutableHashSet<>;false;Clear;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableHashSet<>;false;Except;(System.Collections.Generic.IEnumerable);;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableHashSet<>;false;Intersect;(System.Collections.Generic.IEnumerable);;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableHashSet<>;false;Remove;(T);;Argument[Qualifier];ReturnValue;taint;generated", @@ -1179,9 +1170,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Collections.Immutable;ImmutableHashSet<>;false;get_KeyComparer;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableHashSet<>;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated", "System.Collections.Immutable;ImmutableInterlocked;false;GetOrAdd<,>;(System.Collections.Immutable.ImmutableDictionary,TKey,TValue);;Argument[2];ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableList;false;Create<>;(T);;Argument[0];ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableList;false;Create<>;(T[]);;Argument[0].Element;ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableList;false;CreateRange<>;(System.Collections.Generic.IEnumerable);;Argument[0].Element;ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableList;false;Remove<>;(System.Collections.Immutable.IImmutableList,T);;Argument[0].Element;ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableList;false;RemoveRange<>;(System.Collections.Immutable.IImmutableList,System.Collections.Generic.IEnumerable);;Argument[0].Element;ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableList;false;Replace<>;(System.Collections.Immutable.IImmutableList,T,T);;Argument[0].Element;ReturnValue;taint;generated", @@ -1240,12 +1228,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Collections.Immutable;ImmutableSortedDictionary;false;CreateBuilder<,>;(System.Collections.Generic.IComparer);;Argument[0];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedDictionary;false;CreateBuilder<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEqualityComparer);;Argument[0];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedDictionary;false;CreateBuilder<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEqualityComparer);;Argument[1];ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEnumerable>);;Argument[0];ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEnumerable>);;Argument[1].Element;ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[0];ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[1];ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[2].Element;ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IEnumerable>);;Argument[0].Element;ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedDictionary;false;ToImmutableSortedDictionary<,>;(System.Collections.Generic.IEnumerable>);;Argument[0].Element;ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedDictionary;false;ToImmutableSortedDictionary<,>;(System.Collections.Generic.IEnumerable>,System.Collections.Generic.IComparer);;Argument[0].Element;ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedDictionary;false;ToImmutableSortedDictionary<,>;(System.Collections.Generic.IEnumerable>,System.Collections.Generic.IComparer);;Argument[1];ReturnValue;taint;generated", @@ -1262,8 +1244,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Collections.Immutable;ImmutableSortedDictionary<,>+Builder;false;get_ValueComparer;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedDictionary<,>+Builder;false;set_KeyComparer;(System.Collections.Generic.IComparer);;Argument[0];Argument[Qualifier];taint;generated", "System.Collections.Immutable;ImmutableSortedDictionary<,>+Builder;false;set_ValueComparer;(System.Collections.Generic.IEqualityComparer);;Argument[0];Argument[Qualifier];taint;generated", - "System.Collections.Immutable;ImmutableSortedDictionary<,>+Enumerator;false;get_Current;();;Argument[Qualifier];ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableSortedDictionary<,>;false;Clear;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedDictionary<,>;false;Remove;(TKey);;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedDictionary<,>;false;RemoveRange;(System.Collections.Generic.IEnumerable);;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedDictionary<,>;false;SetItem;(TKey,TValue);;Argument[0];ReturnValue;taint;generated", @@ -1281,10 +1261,7 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Collections.Immutable;ImmutableSortedDictionary<,>;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated", "System.Collections.Immutable;ImmutableSortedDictionary<,>;false;get_ValueComparer;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedSet;false;Create<>;(System.Collections.Generic.IComparer);;Argument[0];ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableSortedSet;false;Create<>;(System.Collections.Generic.IComparer,T);;Argument[0];ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableSortedSet;false;Create<>;(System.Collections.Generic.IComparer,T);;Argument[1];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedSet;false;Create<>;(System.Collections.Generic.IComparer,T[]);;Argument[0];ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableSortedSet;false;Create<>;(T);;Argument[0];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedSet;false;CreateBuilder<>;(System.Collections.Generic.IComparer);;Argument[0];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedSet;false;CreateRange<>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEnumerable);;Argument[0];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedSet;false;CreateRange<>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEnumerable);;Argument[1].Element;ReturnValue;taint;generated", @@ -1294,7 +1271,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Collections.Immutable;ImmutableSortedSet;false;ToImmutableSortedSet<>;(System.Collections.Generic.IEnumerable,System.Collections.Generic.IComparer);;Argument[1];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedSet;false;ToImmutableSortedSet<>;(System.Collections.Immutable.ImmutableSortedSet+Builder);;Argument[0].Element;ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;IntersectWith;(System.Collections.Generic.IEnumerable);;Argument[0].Element;Argument[Qualifier];taint;generated", - "System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;SymmetricExceptWith;(System.Collections.Generic.IEnumerable);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;ToImmutable;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;TryGetValue;(T,T);;Argument[0];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;TryGetValue;(T,T);;Argument[Qualifier];ReturnValue;taint;generated", @@ -1305,13 +1281,8 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;set_KeyComparer;(System.Collections.Generic.IComparer);;Argument[0];Argument[Qualifier];taint;generated", "System.Collections.Immutable;ImmutableSortedSet<>+Enumerator;false;get_Current;();;Argument[Qualifier];ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableSortedSet<>;false;Clear;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedSet<>;false;Except;(System.Collections.Generic.IEnumerable);;Argument[Qualifier];ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableSortedSet<>;false;Intersect;(System.Collections.Generic.IEnumerable);;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedSet<>;false;Remove;(T);;Argument[Qualifier];ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableSortedSet<>;false;SymmetricExcept;(System.Collections.Generic.IEnumerable);;Argument[0].Element;Argument[Qualifier];taint;generated", - "System.Collections.Immutable;ImmutableSortedSet<>;false;SymmetricExcept;(System.Collections.Generic.IEnumerable);;Argument[0].Element;ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableSortedSet<>;false;SymmetricExcept;(System.Collections.Generic.IEnumerable);;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedSet<>;false;ToBuilder;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedSet<>;false;TryGetValue;(T,T);;Argument[0];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedSet<>;false;TryGetValue;(T,T);;Argument[Qualifier];ReturnValue;taint;generated", @@ -1335,23 +1306,17 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Collections.Immutable;ImmutableStack<>;false;Push;(T);;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.ObjectModel;Collection<>;false;Collection;(System.Collections.Generic.IList);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.Collections.ObjectModel;Collection<>;false;InsertItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated", - "System.Collections.ObjectModel;Collection<>;false;InsertItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated", "System.Collections.ObjectModel;Collection<>;false;SetItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated", - "System.Collections.ObjectModel;Collection<>;false;SetItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated", "System.Collections.ObjectModel;Collection<>;false;get_Items;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.ObjectModel;Collection<>;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.ObjectModel;KeyedCollection<,>;false;InsertItem;(System.Int32,TItem);;Argument[1];Argument[Qualifier];taint;generated", - "System.Collections.ObjectModel;KeyedCollection<,>;false;InsertItem;(System.Int32,TItem);;Argument[Qualifier];Argument[1];taint;generated", "System.Collections.ObjectModel;KeyedCollection<,>;false;KeyedCollection;(System.Collections.Generic.IEqualityComparer,System.Int32);;Argument[0];Argument[Qualifier];taint;generated", "System.Collections.ObjectModel;KeyedCollection<,>;false;SetItem;(System.Int32,TItem);;Argument[1];Argument[Qualifier];taint;generated", - "System.Collections.ObjectModel;KeyedCollection<,>;false;SetItem;(System.Int32,TItem);;Argument[Qualifier];Argument[1];taint;generated", "System.Collections.ObjectModel;KeyedCollection<,>;false;TryGetValue;(TKey,TItem);;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.ObjectModel;KeyedCollection<,>;false;get_Comparer;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.ObjectModel;KeyedCollection<,>;false;get_Dictionary;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.ObjectModel;ObservableCollection<>;false;InsertItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated", - "System.Collections.ObjectModel;ObservableCollection<>;false;InsertItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated", "System.Collections.ObjectModel;ObservableCollection<>;false;SetItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated", - "System.Collections.ObjectModel;ObservableCollection<>;false;SetItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated", "System.Collections.ObjectModel;ReadOnlyCollection<>;false;ReadOnlyCollection;(System.Collections.Generic.IList);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.Collections.ObjectModel;ReadOnlyCollection<>;false;get_Items;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.ObjectModel;ReadOnlyCollection<>;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;taint;generated", @@ -1430,7 +1395,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Collections;BitArray;false;Xor;(System.Collections.BitArray);;Argument[Qualifier];ReturnValue;value;generated", "System.Collections;BitArray;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated", "System.Collections;CollectionBase;false;Remove;(System.Object);;Argument[0];Argument[Qualifier];taint;generated", - "System.Collections;CollectionBase;false;Remove;(System.Object);;Argument[Qualifier];Argument[0];taint;generated", "System.Collections;CollectionBase;false;get_InnerList;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections;CollectionBase;false;get_List;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections;CollectionBase;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;taint;generated", @@ -1480,7 +1444,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Collections;Stack;false;Synchronized;(System.Collections.Stack);;Argument[0].Element;ReturnValue;taint;generated", "System.Collections;Stack;false;ToArray;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections;Stack;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated", - "System.ComponentModel.Composition.Hosting;AggregateCatalog;false;GetExports;(System.ComponentModel.Composition.Primitives.ImportDefinition);;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel.Composition.Hosting;AggregateCatalog;false;get_Catalogs;();;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel.Composition.Hosting;AggregateExportProvider;false;AggregateExportProvider;(System.ComponentModel.Composition.Hosting.ExportProvider[]);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.ComponentModel.Composition.Hosting;AggregateExportProvider;false;GetExportsCore;(System.ComponentModel.Composition.Primitives.ImportDefinition,System.ComponentModel.Composition.Hosting.AtomicComposition);;Argument[Qualifier];ReturnValue;taint;generated", @@ -1489,7 +1452,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.ComponentModel.Composition.Hosting;ApplicationCatalog;false;ApplicationCatalog;(System.Reflection.ReflectionContext);;Argument[0];Argument[Qualifier];taint;generated", "System.ComponentModel.Composition.Hosting;ApplicationCatalog;false;ApplicationCatalog;(System.Reflection.ReflectionContext,System.ComponentModel.Composition.Primitives.ICompositionElement);;Argument[0];Argument[Qualifier];taint;generated", "System.ComponentModel.Composition.Hosting;ApplicationCatalog;false;ApplicationCatalog;(System.Reflection.ReflectionContext,System.ComponentModel.Composition.Primitives.ICompositionElement);;Argument[1];Argument[Qualifier];taint;generated", - "System.ComponentModel.Composition.Hosting;ApplicationCatalog;false;GetExports;(System.ComponentModel.Composition.Primitives.ImportDefinition);;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel.Composition.Hosting;AssemblyCatalog;false;AssemblyCatalog;(System.Reflection.Assembly);;Argument[0];Argument[Qualifier];taint;generated", "System.ComponentModel.Composition.Hosting;AssemblyCatalog;false;AssemblyCatalog;(System.Reflection.Assembly,System.ComponentModel.Composition.Primitives.ICompositionElement);;Argument[0];Argument[Qualifier];taint;generated", "System.ComponentModel.Composition.Hosting;AssemblyCatalog;false;AssemblyCatalog;(System.Reflection.Assembly,System.ComponentModel.Composition.Primitives.ICompositionElement);;Argument[1];Argument[Qualifier];taint;generated", @@ -1502,7 +1464,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.ComponentModel.Composition.Hosting;AssemblyCatalog;false;AssemblyCatalog;(System.String,System.Reflection.ReflectionContext);;Argument[1];Argument[Qualifier];taint;generated", "System.ComponentModel.Composition.Hosting;AssemblyCatalog;false;AssemblyCatalog;(System.String,System.Reflection.ReflectionContext,System.ComponentModel.Composition.Primitives.ICompositionElement);;Argument[1];Argument[Qualifier];taint;generated", "System.ComponentModel.Composition.Hosting;AssemblyCatalog;false;AssemblyCatalog;(System.String,System.Reflection.ReflectionContext,System.ComponentModel.Composition.Primitives.ICompositionElement);;Argument[2];Argument[Qualifier];taint;generated", - "System.ComponentModel.Composition.Hosting;AssemblyCatalog;false;GetExports;(System.ComponentModel.Composition.Primitives.ImportDefinition);;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel.Composition.Hosting;AssemblyCatalog;false;ToString;();;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel.Composition.Hosting;AssemblyCatalog;false;get_Assembly;();;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel.Composition.Hosting;AssemblyCatalog;false;get_DisplayName;();;Argument[Qualifier];ReturnValue;taint;generated", @@ -1539,7 +1500,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.ComponentModel.Composition.Hosting;CompositionScopeDefinition;false;CompositionScopeDefinition;(System.ComponentModel.Composition.Primitives.ComposablePartCatalog,System.Collections.Generic.IEnumerable,System.Collections.Generic.IEnumerable);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.ComponentModel.Composition.Hosting;CompositionScopeDefinition;false;CompositionScopeDefinition;(System.ComponentModel.Composition.Primitives.ComposablePartCatalog,System.Collections.Generic.IEnumerable,System.Collections.Generic.IEnumerable);;Argument[1].Element;Argument[Qualifier];taint;generated", "System.ComponentModel.Composition.Hosting;CompositionScopeDefinition;false;CompositionScopeDefinition;(System.ComponentModel.Composition.Primitives.ComposablePartCatalog,System.Collections.Generic.IEnumerable,System.Collections.Generic.IEnumerable);;Argument[2].Element;Argument[Qualifier];taint;generated", - "System.ComponentModel.Composition.Hosting;CompositionScopeDefinition;false;GetExports;(System.ComponentModel.Composition.Primitives.ImportDefinition);;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel.Composition.Hosting;CompositionScopeDefinition;false;get_Children;();;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel.Composition.Hosting;CompositionScopeDefinition;false;get_PublicSurface;();;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel.Composition.Hosting;DirectoryCatalog;false;DirectoryCatalog;(System.String,System.String);;Argument[0];Argument[Qualifier];taint;generated", @@ -1554,7 +1514,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.ComponentModel.Composition.Hosting;DirectoryCatalog;false;DirectoryCatalog;(System.String,System.String,System.Reflection.ReflectionContext,System.ComponentModel.Composition.Primitives.ICompositionElement);;Argument[1];Argument[Qualifier];taint;generated", "System.ComponentModel.Composition.Hosting;DirectoryCatalog;false;DirectoryCatalog;(System.String,System.String,System.Reflection.ReflectionContext,System.ComponentModel.Composition.Primitives.ICompositionElement);;Argument[2];Argument[Qualifier];taint;generated", "System.ComponentModel.Composition.Hosting;DirectoryCatalog;false;DirectoryCatalog;(System.String,System.String,System.Reflection.ReflectionContext,System.ComponentModel.Composition.Primitives.ICompositionElement);;Argument[3];Argument[Qualifier];taint;generated", - "System.ComponentModel.Composition.Hosting;DirectoryCatalog;false;GetExports;(System.ComponentModel.Composition.Primitives.ImportDefinition);;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel.Composition.Hosting;DirectoryCatalog;false;ToString;();;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel.Composition.Hosting;DirectoryCatalog;false;get_DisplayName;();;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel.Composition.Hosting;DirectoryCatalog;false;get_FullPath;();;Argument[Qualifier];ReturnValue;taint;generated", @@ -1573,14 +1532,12 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.ComponentModel.Composition.Hosting;ExportsChangeEventArgs;false;get_AddedExports;();;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel.Composition.Hosting;ExportsChangeEventArgs;false;get_ChangedContractNames;();;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel.Composition.Hosting;ExportsChangeEventArgs;false;get_RemovedExports;();;Argument[Qualifier];ReturnValue;taint;generated", - "System.ComponentModel.Composition.Hosting;FilteredCatalog;false;GetExports;(System.ComponentModel.Composition.Primitives.ImportDefinition);;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel.Composition.Hosting;FilteredCatalog;false;get_Complement;();;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel.Composition.Hosting;ImportEngine;false;ImportEngine;(System.ComponentModel.Composition.Hosting.ExportProvider,System.ComponentModel.Composition.Hosting.CompositionOptions);;Argument[0];Argument[Qualifier];taint;generated", "System.ComponentModel.Composition.Hosting;TypeCatalog;false;TypeCatalog;(System.Collections.Generic.IEnumerable);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.ComponentModel.Composition.Hosting;TypeCatalog;false;TypeCatalog;(System.Collections.Generic.IEnumerable,System.ComponentModel.Composition.Primitives.ICompositionElement);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.ComponentModel.Composition.Hosting;TypeCatalog;false;TypeCatalog;(System.Collections.Generic.IEnumerable,System.ComponentModel.Composition.Primitives.ICompositionElement);;Argument[1];Argument[Qualifier];taint;generated", "System.ComponentModel.Composition.Hosting;TypeCatalog;false;TypeCatalog;(System.Collections.Generic.IEnumerable,System.Reflection.ReflectionContext,System.ComponentModel.Composition.Primitives.ICompositionElement);;Argument[2];Argument[Qualifier];taint;generated", - "System.ComponentModel.Composition.Primitives;ComposablePartCatalog;true;GetExports;(System.ComponentModel.Composition.Primitives.ImportDefinition);;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel.Composition.Primitives;ComposablePartCatalog;true;get_Parts;();;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel.Composition.Primitives;ComposablePartException;false;ComposablePartException;(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext);;Argument[0];Argument[Qualifier];taint;generated", "System.ComponentModel.Composition.Primitives;ComposablePartException;false;ComposablePartException;(System.String,System.ComponentModel.Composition.Primitives.ICompositionElement,System.Exception);;Argument[1];Argument[Qualifier];taint;generated", @@ -1781,9 +1738,7 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.ComponentModel;BaseNumberConverter;false;ConvertTo;(System.ComponentModel.ITypeDescriptorContext,System.Globalization.CultureInfo,System.Object,System.Type);;Argument[1];ReturnValue;taint;generated", "System.ComponentModel;BaseNumberConverter;false;ConvertTo;(System.ComponentModel.ITypeDescriptorContext,System.Globalization.CultureInfo,System.Object,System.Type);;Argument[2];ReturnValue;taint;generated", "System.ComponentModel;BindingList<>;false;InsertItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated", - "System.ComponentModel;BindingList<>;false;InsertItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated", "System.ComponentModel;BindingList<>;false;SetItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated", - "System.ComponentModel;BindingList<>;false;SetItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated", "System.ComponentModel;CategoryAttribute;false;CategoryAttribute;(System.String);;Argument[0];Argument[Qualifier];taint;generated", "System.ComponentModel;CategoryAttribute;false;get_Category;();;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel;CharConverter;false;ConvertTo;(System.ComponentModel.ITypeDescriptorContext,System.Globalization.CultureInfo,System.Object,System.Type);;Argument[2];ReturnValue;taint;generated", @@ -1917,7 +1872,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.ComponentModel;ToolboxItemAttribute;false;get_ToolboxItemTypeName;();;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel;ToolboxItemFilterAttribute;false;get_TypeId;();;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel;TypeConverter+StandardValuesCollection;false;CopyTo;(System.Array,System.Int32);;Argument[Qualifier];Argument[0].Element;taint;generated", - "System.ComponentModel;TypeConverter+StandardValuesCollection;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel;TypeConverter+StandardValuesCollection;false;StandardValuesCollection;(System.Collections.ICollection);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.ComponentModel;TypeConverter+StandardValuesCollection;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel;TypeConverter;false;ConvertFrom;(System.Object);;Argument[0];ReturnValue;taint;generated", @@ -2380,10 +2334,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Data.Common;DataTableMappingCollection;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated", "System.Data.Common;DbCommand;false;ExecuteReader;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Data.Common;DbCommand;false;ExecuteReader;(System.Data.CommandBehavior);;Argument[Qualifier];ReturnValue;taint;generated", - "System.Data.Common;DbCommand;false;ExecuteReaderAsync;();;Argument[Qualifier];ReturnValue;taint;generated", - "System.Data.Common;DbCommand;false;ExecuteReaderAsync;(System.Data.CommandBehavior);;Argument[Qualifier];ReturnValue;taint;generated", - "System.Data.Common;DbCommand;false;ExecuteReaderAsync;(System.Data.CommandBehavior,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", - "System.Data.Common;DbCommand;false;ExecuteReaderAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.Data.Common;DbCommand;false;get_Connection;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Data.Common;DbCommand;false;get_Parameters;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Data.Common;DbCommand;false;get_Transaction;();;Argument[Qualifier];ReturnValue;taint;generated", @@ -2391,7 +2341,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Data.Common;DbCommand;false;set_Connection;(System.Data.IDbConnection);;Argument[0];Argument[Qualifier];taint;generated", "System.Data.Common;DbCommand;false;set_Transaction;(System.Data.Common.DbTransaction);;Argument[0];Argument[Qualifier];taint;generated", "System.Data.Common;DbCommand;false;set_Transaction;(System.Data.IDbTransaction);;Argument[0];Argument[Qualifier];taint;generated", - "System.Data.Common;DbCommand;true;ExecuteDbDataReaderAsync;(System.Data.CommandBehavior,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.Data.Common;DbCommand;true;PrepareAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", "System.Data.Common;DbCommandBuilder;false;GetDeleteCommand;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Data.Common;DbCommandBuilder;false;GetDeleteCommand;(System.Boolean);;Argument[Qualifier];ReturnValue;taint;generated", @@ -2418,7 +2367,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Data.Common;DbConnectionStringBuilder;false;AppendKeyValuePair;(System.Text.StringBuilder,System.String,System.String);;Argument[2];Argument[0];taint;generated", "System.Data.Common;DbConnectionStringBuilder;false;AppendKeyValuePair;(System.Text.StringBuilder,System.String,System.String,System.Boolean);;Argument[1];Argument[0];taint;generated", "System.Data.Common;DbConnectionStringBuilder;false;AppendKeyValuePair;(System.Text.StringBuilder,System.String,System.String,System.Boolean);;Argument[2];Argument[0];taint;generated", - "System.Data.Common;DbConnectionStringBuilder;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Data.Common;DbConnectionStringBuilder;false;GetProperties;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Data.Common;DbConnectionStringBuilder;false;GetProperties;(System.Attribute[]);;Argument[Qualifier];ReturnValue;taint;generated", "System.Data.Common;DbConnectionStringBuilder;false;GetPropertyOwner;(System.ComponentModel.PropertyDescriptor);;Argument[Qualifier];ReturnValue;value;generated", @@ -2445,12 +2393,9 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Data.Common;DbDataAdapter;true;CreateRowUpdatingEvent;(System.Data.DataRow,System.Data.IDbCommand,System.Data.StatementType,System.Data.Common.DataTableMapping);;Argument[0];ReturnValue;taint;generated", "System.Data.Common;DbDataAdapter;true;CreateRowUpdatingEvent;(System.Data.DataRow,System.Data.IDbCommand,System.Data.StatementType,System.Data.Common.DataTableMapping);;Argument[1];ReturnValue;taint;generated", "System.Data.Common;DbDataAdapter;true;CreateRowUpdatingEvent;(System.Data.DataRow,System.Data.IDbCommand,System.Data.StatementType,System.Data.Common.DataTableMapping);;Argument[3];ReturnValue;taint;generated", - "System.Data.Common;DbDataReader;false;GetFieldValueAsync<>;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", "System.Data.Common;DbDataReader;true;GetFieldValue<>;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", - "System.Data.Common;DbDataReader;true;GetFieldValueAsync<>;(System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.Data.Common;DbDataReader;true;GetProviderSpecificValue;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", "System.Data.Common;DbDataReader;true;GetProviderSpecificValues;(System.Object[]);;Argument[Qualifier];Argument[0].Element;taint;generated", - "System.Data.Common;DbDataReader;true;GetSchemaTableAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.Data.Common;DbDataReader;true;GetTextReader;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", "System.Data.Common;DbDataRecord;false;GetPropertyOwner;(System.ComponentModel.PropertyDescriptor);;Argument[Qualifier];ReturnValue;value;generated", "System.Data.Common;DbEnumerator;false;DbEnumerator;(System.Data.IDataReader);;Argument[0];Argument[Qualifier];taint;generated", @@ -2595,7 +2540,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Data.Odbc;OdbcParameter;false;set_Value;(System.Object);;Argument[0];Argument[Qualifier];taint;generated", "System.Data.Odbc;OdbcParameterCollection;false;Add;(System.Data.Odbc.OdbcParameter);;Argument[0];Argument[Qualifier];taint;generated", "System.Data.Odbc;OdbcParameterCollection;false;Add;(System.Data.Odbc.OdbcParameter);;Argument[0];ReturnValue;taint;generated", - "System.Data.Odbc;OdbcParameterCollection;false;Add;(System.Data.Odbc.OdbcParameter);;Argument[Qualifier];Argument[0];taint;generated", "System.Data.Odbc;OdbcParameterCollection;false;Add;(System.String,System.Data.Odbc.OdbcType);;Argument[0];Argument[Qualifier];taint;generated", "System.Data.Odbc;OdbcParameterCollection;false;Add;(System.String,System.Data.Odbc.OdbcType);;Argument[0];ReturnValue;taint;generated", "System.Data.Odbc;OdbcParameterCollection;false;Add;(System.String,System.Data.Odbc.OdbcType,System.Int32);;Argument[0];Argument[Qualifier];taint;generated", @@ -2617,7 +2561,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Data.Odbc;OdbcParameterCollection;false;GetParameter;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", "System.Data.Odbc;OdbcParameterCollection;false;GetParameter;(System.String);;Argument[Qualifier];ReturnValue;taint;generated", "System.Data.Odbc;OdbcParameterCollection;false;Insert;(System.Int32,System.Data.Odbc.OdbcParameter);;Argument[1];Argument[Qualifier];taint;generated", - "System.Data.Odbc;OdbcParameterCollection;false;Insert;(System.Int32,System.Data.Odbc.OdbcParameter);;Argument[Qualifier];Argument[1];taint;generated", "System.Data.Odbc;OdbcParameterCollection;false;SetParameter;(System.Int32,System.Data.Common.DbParameter);;Argument[Qualifier];Argument[1];taint;generated", "System.Data.Odbc;OdbcParameterCollection;false;SetParameter;(System.String,System.Data.Common.DbParameter);;Argument[Qualifier];Argument[1];taint;generated", "System.Data.Odbc;OdbcParameterCollection;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", @@ -2729,15 +2672,11 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Data;DataColumn;false;set_Prefix;(System.String);;Argument[0];Argument[Qualifier];taint;generated", "System.Data;DataColumnChangeEventArgs;false;DataColumnChangeEventArgs;(System.Data.DataRow,System.Data.DataColumn,System.Object);;Argument[1];Argument[Qualifier];taint;generated", "System.Data;DataColumnChangeEventArgs;false;get_Column;();;Argument[Qualifier];ReturnValue;taint;generated", - "System.Data;DataColumnCollection;false;Add;();;Argument[Qualifier];ReturnValue;taint;generated", - "System.Data;DataColumnCollection;false;Add;(System.String,System.Type);;Argument[Qualifier];ReturnValue;taint;generated", - "System.Data;DataColumnCollection;false;Add;(System.String,System.Type,System.String);;Argument[Qualifier];ReturnValue;taint;generated", "System.Data;DataColumnCollection;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", "System.Data;DataColumnCollection;false;get_Item;(System.String);;Argument[Qualifier];ReturnValue;taint;generated", "System.Data;DataColumnCollection;false;get_List;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Data;DataReaderExtensions;false;GetDateTime;(System.Data.Common.DbDataReader,System.String);;Argument[0].Element;ReturnValue;taint;generated", "System.Data;DataReaderExtensions;false;GetFieldValue<>;(System.Data.Common.DbDataReader,System.String);;Argument[0].Element;ReturnValue;taint;generated", - "System.Data;DataReaderExtensions;false;GetFieldValueAsync<>;(System.Data.Common.DbDataReader,System.String,System.Threading.CancellationToken);;Argument[0].Element;ReturnValue;taint;generated", "System.Data;DataReaderExtensions;false;GetGuid;(System.Data.Common.DbDataReader,System.String);;Argument[0].Element;ReturnValue;taint;generated", "System.Data;DataReaderExtensions;false;GetProviderSpecificValue;(System.Data.Common.DbDataReader,System.String);;Argument[0].Element;ReturnValue;taint;generated", "System.Data;DataReaderExtensions;false;GetString;(System.Data.Common.DbDataReader,System.String);;Argument[0].Element;ReturnValue;taint;generated", @@ -2767,16 +2706,10 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Data;DataRelation;false;get_RelationName;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Data;DataRelation;false;set_RelationName;(System.String);;Argument[0];Argument[Qualifier];taint;generated", "System.Data;DataRelationCollection;false;Remove;(System.Data.DataRelation);;Argument[0];Argument[Qualifier];taint;generated", - "System.Data;DataRelationCollection;true;Add;(System.Data.DataColumn,System.Data.DataColumn);;Argument[Qualifier];ReturnValue;taint;generated", - "System.Data;DataRelationCollection;true;Add;(System.Data.DataColumn[],System.Data.DataColumn[]);;Argument[Qualifier];ReturnValue;taint;generated", - "System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn,System.Data.DataColumn);;Argument[Qualifier];ReturnValue;taint;generated", "System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn,System.Data.DataColumn,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated", "System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn,System.Data.DataColumn,System.Boolean);;Argument[0];ReturnValue;taint;generated", - "System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn,System.Data.DataColumn,System.Boolean);;Argument[Qualifier];ReturnValue;taint;generated", - "System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn[],System.Data.DataColumn[]);;Argument[Qualifier];ReturnValue;taint;generated", "System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn[],System.Data.DataColumn[],System.Boolean);;Argument[0];Argument[Qualifier];taint;generated", "System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn[],System.Data.DataColumn[],System.Boolean);;Argument[0];ReturnValue;taint;generated", - "System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn[],System.Data.DataColumn[],System.Boolean);;Argument[Qualifier];ReturnValue;taint;generated", "System.Data;DataRow;false;DataRow;(System.Data.DataRowBuilder);;Argument[0];Argument[Qualifier];taint;generated", "System.Data;DataRow;false;GetChildRows;(System.Data.DataRelation);;Argument[Qualifier];ReturnValue;taint;generated", "System.Data;DataRow;false;GetChildRows;(System.Data.DataRelation,System.Data.DataRowVersion);;Argument[Qualifier];ReturnValue;taint;generated", @@ -2874,10 +2807,8 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Data;DataTable;false;set_PrimaryKey;(System.Data.DataColumn[]);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.Data;DataTable;false;set_Site;(System.ComponentModel.ISite);;Argument[0];Argument[Qualifier];taint;generated", "System.Data;DataTable;false;set_TableName;(System.String);;Argument[0];Argument[Qualifier];taint;generated", - "System.Data;DataTableCollection;false;Add;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Data;DataTableCollection;false;Add;(System.String,System.String);;Argument[1];Argument[Qualifier];taint;generated", "System.Data;DataTableCollection;false;Add;(System.String,System.String);;Argument[1];ReturnValue;taint;generated", - "System.Data;DataTableCollection;false;Add;(System.String,System.String);;Argument[Qualifier];ReturnValue;taint;generated", "System.Data;DataTableCollection;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", "System.Data;DataTableCollection;false;get_Item;(System.String);;Argument[Qualifier];ReturnValue;taint;generated", "System.Data;DataTableCollection;false;get_Item;(System.String,System.String);;Argument[Qualifier];ReturnValue;taint;generated", @@ -3180,29 +3111,20 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.DirectoryServices.Protocols;DirSyncRequestControl;false;DirSyncRequestControl;(System.Byte[]);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.DirectoryServices.Protocols;DirSyncRequestControl;false;set_Cookie;(System.Byte[]);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.DirectoryServices.Protocols;DirectoryAttribute;false;Add;(System.Byte[]);;Argument[0].Element;Argument[Qualifier];taint;generated", - "System.DirectoryServices.Protocols;DirectoryAttribute;false;Add;(System.Byte[]);;Argument[Qualifier];Argument[0].Element;taint;generated", "System.DirectoryServices.Protocols;DirectoryAttribute;false;Add;(System.String);;Argument[0];Argument[Qualifier];taint;generated", - "System.DirectoryServices.Protocols;DirectoryAttribute;false;Add;(System.String);;Argument[Qualifier];Argument[0];taint;generated", "System.DirectoryServices.Protocols;DirectoryAttribute;false;Add;(System.Uri);;Argument[0];Argument[Qualifier];taint;generated", - "System.DirectoryServices.Protocols;DirectoryAttribute;false;Add;(System.Uri);;Argument[Qualifier];Argument[0];taint;generated", "System.DirectoryServices.Protocols;DirectoryAttribute;false;AddRange;(System.Object[]);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.DirectoryServices.Protocols;DirectoryAttribute;false;CopyTo;(System.Object[],System.Int32);;Argument[Qualifier];Argument[0].Element;taint;generated", "System.DirectoryServices.Protocols;DirectoryAttribute;false;DirectoryAttribute;(System.String,System.Object[]);;Argument[0];Argument[Qualifier];taint;generated", "System.DirectoryServices.Protocols;DirectoryAttribute;false;DirectoryAttribute;(System.String,System.Object[]);;Argument[1].Element;Argument[Qualifier];taint;generated", - "System.DirectoryServices.Protocols;DirectoryAttribute;false;DirectoryAttribute;(System.String,System.Object[]);;Argument[Qualifier];Argument[1].Element;taint;generated", "System.DirectoryServices.Protocols;DirectoryAttribute;false;GetValues;(System.Type);;Argument[Qualifier];ReturnValue;taint;generated", "System.DirectoryServices.Protocols;DirectoryAttribute;false;Insert;(System.Int32,System.Byte[]);;Argument[1].Element;Argument[Qualifier];taint;generated", - "System.DirectoryServices.Protocols;DirectoryAttribute;false;Insert;(System.Int32,System.Byte[]);;Argument[Qualifier];Argument[1].Element;taint;generated", "System.DirectoryServices.Protocols;DirectoryAttribute;false;Insert;(System.Int32,System.String);;Argument[1];Argument[Qualifier];taint;generated", - "System.DirectoryServices.Protocols;DirectoryAttribute;false;Insert;(System.Int32,System.String);;Argument[Qualifier];Argument[1];taint;generated", "System.DirectoryServices.Protocols;DirectoryAttribute;false;Insert;(System.Int32,System.Uri);;Argument[1];Argument[Qualifier];taint;generated", - "System.DirectoryServices.Protocols;DirectoryAttribute;false;Insert;(System.Int32,System.Uri);;Argument[Qualifier];Argument[1];taint;generated", "System.DirectoryServices.Protocols;DirectoryAttribute;false;Remove;(System.Object);;Argument[0];Argument[Qualifier];taint;generated", - "System.DirectoryServices.Protocols;DirectoryAttribute;false;Remove;(System.Object);;Argument[Qualifier];Argument[0];taint;generated", "System.DirectoryServices.Protocols;DirectoryAttribute;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", "System.DirectoryServices.Protocols;DirectoryAttribute;false;get_Name;();;Argument[Qualifier];ReturnValue;taint;generated", "System.DirectoryServices.Protocols;DirectoryAttribute;false;set_Item;(System.Int32,System.Object);;Argument[1];Argument[Qualifier];taint;generated", - "System.DirectoryServices.Protocols;DirectoryAttribute;false;set_Item;(System.Int32,System.Object);;Argument[Qualifier];Argument[1];taint;generated", "System.DirectoryServices.Protocols;DirectoryAttribute;false;set_Name;(System.String);;Argument[0];Argument[Qualifier];taint;generated", "System.DirectoryServices.Protocols;DirectoryAttributeCollection;false;Add;(System.DirectoryServices.Protocols.DirectoryAttribute);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.DirectoryServices.Protocols;DirectoryAttributeCollection;false;AddRange;(System.DirectoryServices.Protocols.DirectoryAttributeCollection);;Argument[0].Element;Argument[Qualifier];taint;generated", @@ -3391,22 +3313,18 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Drawing.Printing;PrintPageEventArgs;false;get_PageBounds;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Drawing.Printing;PrintPageEventArgs;false;get_PageSettings;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Drawing.Printing;PrinterSettings+PaperSizeCollection;false;Add;(System.Drawing.Printing.PaperSize);;Argument[0];Argument[Qualifier];taint;generated", - "System.Drawing.Printing;PrinterSettings+PaperSizeCollection;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Drawing.Printing;PrinterSettings+PaperSizeCollection;false;PaperSizeCollection;(System.Drawing.Printing.PaperSize[]);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.Drawing.Printing;PrinterSettings+PaperSizeCollection;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", "System.Drawing.Printing;PrinterSettings+PaperSizeCollection;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated", "System.Drawing.Printing;PrinterSettings+PaperSourceCollection;false;Add;(System.Drawing.Printing.PaperSource);;Argument[0];Argument[Qualifier];taint;generated", - "System.Drawing.Printing;PrinterSettings+PaperSourceCollection;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Drawing.Printing;PrinterSettings+PaperSourceCollection;false;PaperSourceCollection;(System.Drawing.Printing.PaperSource[]);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.Drawing.Printing;PrinterSettings+PaperSourceCollection;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", "System.Drawing.Printing;PrinterSettings+PaperSourceCollection;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated", "System.Drawing.Printing;PrinterSettings+PrinterResolutionCollection;false;Add;(System.Drawing.Printing.PrinterResolution);;Argument[0];Argument[Qualifier];taint;generated", - "System.Drawing.Printing;PrinterSettings+PrinterResolutionCollection;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Drawing.Printing;PrinterSettings+PrinterResolutionCollection;false;PrinterResolutionCollection;(System.Drawing.Printing.PrinterResolution[]);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.Drawing.Printing;PrinterSettings+PrinterResolutionCollection;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", "System.Drawing.Printing;PrinterSettings+PrinterResolutionCollection;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated", "System.Drawing.Printing;PrinterSettings+StringCollection;false;Add;(System.String);;Argument[0];Argument[Qualifier];taint;generated", - "System.Drawing.Printing;PrinterSettings+StringCollection;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Drawing.Printing;PrinterSettings+StringCollection;false;StringCollection;(System.String[]);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.Drawing.Printing;PrinterSettings+StringCollection;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", "System.Drawing.Printing;PrinterSettings+StringCollection;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated", @@ -3663,14 +3581,10 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.IO.Compression;BrotliStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", "System.IO.Compression;BrotliStream;false;get_BaseStream;();;Argument[Qualifier];ReturnValue;taint;generated", "System.IO.Compression;DeflateStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", - "System.IO.Compression;DeflateStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO.Compression;DeflateStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.IO.Compression;DeflateStream;false;get_BaseStream;();;Argument[Qualifier];ReturnValue;taint;generated", "System.IO.Compression;GZipStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", "System.IO.Compression;GZipStream;false;GZipStream;(System.IO.Stream,System.IO.Compression.CompressionLevel,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated", "System.IO.Compression;GZipStream;false;GZipStream;(System.IO.Stream,System.IO.Compression.CompressionMode,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated", - "System.IO.Compression;GZipStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO.Compression;GZipStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.IO.Compression;GZipStream;false;get_BaseStream;();;Argument[Qualifier];ReturnValue;taint;generated", "System.IO.Compression;ZLibException;false;GetObjectData;(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext);;Argument[Qualifier];Argument[0];taint;generated", "System.IO.Compression;ZLibException;false;ZLibException;(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext);;Argument[0];Argument[Qualifier];taint;generated", @@ -3709,7 +3623,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.IO.IsolatedStorage;IsolatedStorage;false;get_AssemblyIdentity;();;Argument[Qualifier];ReturnValue;taint;generated", "System.IO.IsolatedStorage;IsolatedStorage;false;get_DomainIdentity;();;Argument[Qualifier];ReturnValue;taint;generated", "System.IO.IsolatedStorage;IsolatedStorageFileStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", - "System.IO.IsolatedStorage;IsolatedStorageFileStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.IO.IsolatedStorage;IsolatedStorageFileStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated", "System.IO.IsolatedStorage;IsolatedStorageFileStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.IO.IsolatedStorage;IsolatedStorageFileStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", @@ -3830,7 +3743,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.IO;BinaryReader;false;get_BaseStream;();;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;BinaryWriter;false;BinaryWriter;(System.IO.Stream,System.Text.Encoding,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated", "System.IO;BinaryWriter;false;BinaryWriter;(System.IO.Stream,System.Text.Encoding,System.Boolean);;Argument[1];Argument[Qualifier];taint;generated", - "System.IO;BinaryWriter;false;DisposeAsync;();;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;BinaryWriter;false;Write;(System.Byte[]);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.IO;BinaryWriter;false;Write;(System.Byte[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.IO;BinaryWriter;false;get_BaseStream;();;Argument[Qualifier];ReturnValue;taint;generated", @@ -3899,7 +3811,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.IO;FileNotFoundException;false;ToString;();;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;FileNotFoundException;false;get_Message;();;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;FileStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", - "System.IO;FileStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;FileStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated", "System.IO;FileStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;FileStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", @@ -3956,26 +3867,14 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.IO;RenamedEventArgs;false;RenamedEventArgs;(System.IO.WatcherChangeTypes,System.String,System.String,System.String);;Argument[3];Argument[Qualifier];taint;generated", "System.IO;RenamedEventArgs;false;get_OldFullPath;();;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;RenamedEventArgs;false;get_OldName;();;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO;Stream;false;FlushAsync;();;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;Stream;false;Synchronized;(System.IO.Stream);;Argument[0];ReturnValue;taint;generated", - "System.IO;Stream;true;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO;Stream;true;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO;Stream;true;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;StreamReader;false;StreamReader;(System.IO.Stream,System.Text.Encoding,System.Boolean,System.Int32,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated", "System.IO;StreamReader;false;StreamReader;(System.IO.Stream,System.Text.Encoding,System.Boolean,System.Int32,System.Boolean);;Argument[1];Argument[Qualifier];taint;generated", "System.IO;StreamReader;false;get_BaseStream;();;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;StreamReader;false;get_CurrentEncoding;();;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO;StreamWriter;false;FlushAsync;();;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;StreamWriter;false;StreamWriter;(System.IO.Stream,System.Text.Encoding,System.Int32,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated", "System.IO;StreamWriter;false;StreamWriter;(System.IO.Stream,System.Text.Encoding,System.Int32,System.Boolean);;Argument[1];Argument[Qualifier];taint;generated", - "System.IO;StreamWriter;false;WriteAsync;(System.Char);;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO;StreamWriter;false;WriteAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;ReturnValue;taint;generated", - "System.IO;StreamWriter;false;WriteAsync;(System.Char[],System.Int32,System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO;StreamWriter;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", "System.IO;StreamWriter;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated", - "System.IO;StreamWriter;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO;StreamWriter;false;WriteAsync;(System.String);;Argument[0];ReturnValue;taint;generated", - "System.IO;StreamWriter;false;WriteAsync;(System.String);;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;StreamWriter;false;WriteLine;(System.String,System.Object);;Argument[0];Argument[Qualifier];taint;generated", "System.IO;StreamWriter;false;WriteLine;(System.String,System.Object);;Argument[1];Argument[Qualifier];taint;generated", "System.IO;StreamWriter;false;WriteLine;(System.String,System.Object,System.Object);;Argument[0];Argument[Qualifier];taint;generated", @@ -3987,15 +3886,7 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.IO;StreamWriter;false;WriteLine;(System.String,System.Object,System.Object,System.Object);;Argument[3];Argument[Qualifier];taint;generated", "System.IO;StreamWriter;false;WriteLine;(System.String,System.Object[]);;Argument[0];Argument[Qualifier];taint;generated", "System.IO;StreamWriter;false;WriteLine;(System.String,System.Object[]);;Argument[1].Element;Argument[Qualifier];taint;generated", - "System.IO;StreamWriter;false;WriteLineAsync;();;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO;StreamWriter;false;WriteLineAsync;(System.Char);;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO;StreamWriter;false;WriteLineAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;ReturnValue;taint;generated", - "System.IO;StreamWriter;false;WriteLineAsync;(System.Char[],System.Int32,System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO;StreamWriter;false;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", "System.IO;StreamWriter;false;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated", - "System.IO;StreamWriter;false;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO;StreamWriter;false;WriteLineAsync;(System.String);;Argument[0];ReturnValue;taint;generated", - "System.IO;StreamWriter;false;WriteLineAsync;(System.String);;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;StreamWriter;false;get_BaseStream;();;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;StreamWriter;false;get_Encoding;();;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;StringWriter;false;GetStringBuilder;();;Argument[Qualifier];ReturnValue;taint;generated", @@ -4003,29 +3894,19 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.IO;StringWriter;false;ToString;();;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;StringWriter;false;Write;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.IO;StringWriter;false;Write;(System.String);;Argument[0];Argument[Qualifier];taint;generated", - "System.IO;StringWriter;false;Write;(System.Text.StringBuilder);;Argument[0];Argument[Qualifier];taint;generated", "System.IO;StringWriter;false;WriteAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.IO;StringWriter;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated", "System.IO;StringWriter;false;WriteAsync;(System.String);;Argument[0];Argument[Qualifier];taint;generated", - "System.IO;StringWriter;false;WriteAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[0];Argument[Qualifier];taint;generated", "System.IO;StringWriter;false;WriteAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated", - "System.IO;StringWriter;false;WriteLine;(System.Text.StringBuilder);;Argument[0];Argument[Qualifier];taint;generated", "System.IO;StringWriter;false;WriteLineAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.IO;StringWriter;false;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated", "System.IO;StringWriter;false;WriteLineAsync;(System.String);;Argument[0];Argument[Qualifier];taint;generated", - "System.IO;StringWriter;false;WriteLineAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[0];Argument[Qualifier];taint;generated", "System.IO;StringWriter;false;WriteLineAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated", - "System.IO;StringWriter;false;WriteLineAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;TextReader;false;Synchronized;(System.IO.TextReader);;Argument[0];ReturnValue;taint;generated", "System.IO;TextWriter;false;Synchronized;(System.IO.TextWriter);;Argument[0];ReturnValue;taint;generated", "System.IO;TextWriter;false;TextWriter;(System.IFormatProvider);;Argument[0];Argument[Qualifier];taint;generated", "System.IO;TextWriter;false;WriteAsync;(System.Char[]);;Argument[0].Element;Argument[Qualifier];taint;generated", - "System.IO;TextWriter;false;WriteAsync;(System.Char[]);;Argument[0].Element;ReturnValue;taint;generated", - "System.IO;TextWriter;false;WriteAsync;(System.Char[]);;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;TextWriter;false;WriteLineAsync;(System.Char[]);;Argument[0].Element;Argument[Qualifier];taint;generated", - "System.IO;TextWriter;false;WriteLineAsync;(System.Char[]);;Argument[0].Element;ReturnValue;taint;generated", - "System.IO;TextWriter;false;WriteLineAsync;(System.Char[]);;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO;TextWriter;true;FlushAsync;();;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;TextWriter;true;Write;(System.Char[]);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.IO;TextWriter;true;Write;(System.Object);;Argument[0];Argument[Qualifier];taint;generated", "System.IO;TextWriter;true;Write;(System.String,System.Object);;Argument[0];Argument[Qualifier];taint;generated", @@ -4039,14 +3920,7 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.IO;TextWriter;true;Write;(System.String,System.Object,System.Object,System.Object);;Argument[3];Argument[Qualifier];taint;generated", "System.IO;TextWriter;true;Write;(System.String,System.Object[]);;Argument[0];Argument[Qualifier];taint;generated", "System.IO;TextWriter;true;Write;(System.String,System.Object[]);;Argument[1].Element;Argument[Qualifier];taint;generated", - "System.IO;TextWriter;true;WriteAsync;(System.Char);;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO;TextWriter;true;WriteAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;ReturnValue;taint;generated", - "System.IO;TextWriter;true;WriteAsync;(System.Char[],System.Int32,System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO;TextWriter;true;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", "System.IO;TextWriter;true;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated", - "System.IO;TextWriter;true;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO;TextWriter;true;WriteAsync;(System.String);;Argument[0];ReturnValue;taint;generated", - "System.IO;TextWriter;true;WriteAsync;(System.String);;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;TextWriter;true;WriteAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated", "System.IO;TextWriter;true;WriteLine;(System.Char[]);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.IO;TextWriter;true;WriteLine;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated", @@ -4063,18 +3937,8 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.IO;TextWriter;true;WriteLine;(System.String,System.Object,System.Object,System.Object);;Argument[3];Argument[Qualifier];taint;generated", "System.IO;TextWriter;true;WriteLine;(System.String,System.Object[]);;Argument[0];Argument[Qualifier];taint;generated", "System.IO;TextWriter;true;WriteLine;(System.String,System.Object[]);;Argument[1].Element;Argument[Qualifier];taint;generated", - "System.IO;TextWriter;true;WriteLine;(System.Text.StringBuilder);;Argument[0];Argument[Qualifier];taint;generated", - "System.IO;TextWriter;true;WriteLineAsync;();;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO;TextWriter;true;WriteLineAsync;(System.Char);;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO;TextWriter;true;WriteLineAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;ReturnValue;taint;generated", - "System.IO;TextWriter;true;WriteLineAsync;(System.Char[],System.Int32,System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO;TextWriter;true;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", "System.IO;TextWriter;true;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated", - "System.IO;TextWriter;true;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO;TextWriter;true;WriteLineAsync;(System.String);;Argument[0];ReturnValue;taint;generated", - "System.IO;TextWriter;true;WriteLineAsync;(System.String);;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;TextWriter;true;WriteLineAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated", - "System.IO;TextWriter;true;WriteLineAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;TextWriter;true;get_FormatProvider;();;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;TextWriter;true;get_NewLine;();;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;TextWriter;true;set_NewLine;(System.String);;Argument[0];Argument[Qualifier];taint;generated", @@ -4673,7 +4537,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Net.Http.Headers;HeaderStringValues;false;ToString;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Http.Headers;HttpHeaders;false;get_NonValidated;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Http.Headers;HttpHeadersNonValidated+Enumerator;false;get_Current;();;Argument[Qualifier];ReturnValue;taint;generated", - "System.Net.Http.Headers;HttpHeadersNonValidated;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Http.Headers;HttpHeadersNonValidated;false;TryGetValue;(System.String,System.Net.Http.Headers.HeaderStringValues);;Argument[0];ReturnValue;taint;generated", "System.Net.Http.Headers;HttpHeadersNonValidated;false;TryGetValues;(System.String,System.Net.Http.Headers.HeaderStringValues);;Argument[0];ReturnValue;taint;generated", "System.Net.Http.Headers;HttpHeadersNonValidated;false;get_Item;(System.String);;Argument[0];ReturnValue;taint;generated", @@ -4774,23 +4637,14 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Net.Http;ByteArrayContent;false;ByteArrayContent;(System.Byte[]);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.Net.Http;ByteArrayContent;false;ByteArrayContent;(System.Byte[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.Net.Http;ByteArrayContent;false;CreateContentReadStream;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", - "System.Net.Http;ByteArrayContent;false;CreateContentReadStreamAsync;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Http;ByteArrayContent;false;SerializeToStream;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated", - "System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[0];ReturnValue;taint;generated", "System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[Qualifier];Argument[0];taint;generated", - "System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[Qualifier];ReturnValue;taint;generated", - "System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", - "System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[2];ReturnValue;taint;generated", "System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated", - "System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Http;DelegatingHandler;false;DelegatingHandler;(System.Net.Http.HttpMessageHandler);;Argument[0];Argument[Qualifier];taint;generated", "System.Net.Http;DelegatingHandler;false;SendAsync;(System.Net.Http.HttpRequestMessage,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated", "System.Net.Http;DelegatingHandler;false;get_InnerHandler;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Http;DelegatingHandler;false;set_InnerHandler;(System.Net.Http.HttpMessageHandler);;Argument[0];Argument[Qualifier];taint;generated", - "System.Net.Http;FormUrlEncodedContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", - "System.Net.Http;FormUrlEncodedContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[2];ReturnValue;taint;generated", "System.Net.Http;FormUrlEncodedContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated", - "System.Net.Http;FormUrlEncodedContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Http;HttpClient;false;Send;(System.Net.Http.HttpRequestMessage);;Argument[Qualifier];Argument[0];taint;generated", "System.Net.Http;HttpClient;false;Send;(System.Net.Http.HttpRequestMessage,System.Net.Http.HttpCompletionOption);;Argument[Qualifier];Argument[0];taint;generated", "System.Net.Http;HttpClient;false;Send;(System.Net.Http.HttpRequestMessage,System.Net.Http.HttpCompletionOption,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated", @@ -4817,10 +4671,7 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Net.Http;HttpContent;false;ReadAsStreamAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Http;HttpContent;false;get_Headers;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Http;HttpContent;true;CreateContentReadStream;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", - "System.Net.Http;HttpContent;true;CreateContentReadStreamAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", - "System.Net.Http;HttpContent;true;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", "System.Net.Http;HttpContent;true;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated", - "System.Net.Http;HttpContent;true;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Http;HttpMessageInvoker;false;HttpMessageInvoker;(System.Net.Http.HttpMessageHandler,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated", "System.Net.Http;HttpMessageInvoker;false;SendAsync;(System.Net.Http.HttpRequestMessage,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated", "System.Net.Http;HttpMethod;false;HttpMethod;(System.String);;Argument[0];Argument[Qualifier];taint;generated", @@ -4855,7 +4706,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Net.Http;MultipartFormDataContent;false;Add;(System.Net.Http.HttpContent,System.String,System.String);;Argument[0];Argument[Qualifier];taint;generated", "System.Net.Http;MultipartFormDataContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated", "System.Net.Http;ReadOnlyMemoryContent;false;CreateContentReadStream;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", - "System.Net.Http;ReadOnlyMemoryContent;false;CreateContentReadStreamAsync;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Http;ReadOnlyMemoryContent;false;ReadOnlyMemoryContent;(System.ReadOnlyMemory);;Argument[0];Argument[Qualifier];taint;generated", "System.Net.Http;SocketsHttpConnectionContext;false;get_DnsEndPoint;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Http;SocketsHttpConnectionContext;false;get_InitialRequestMessage;();;Argument[Qualifier];ReturnValue;taint;generated", @@ -4894,19 +4744,11 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Net.Http;SocketsHttpPlaintextStreamFilterContext;false;get_NegotiatedHttpVersion;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Http;SocketsHttpPlaintextStreamFilterContext;false;get_PlaintextStream;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Http;StreamContent;false;SerializeToStream;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated", - "System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[0];ReturnValue;taint;generated", "System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[Qualifier];Argument[0];taint;generated", - "System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[Qualifier];ReturnValue;taint;generated", - "System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", - "System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[2];ReturnValue;taint;generated", "System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated", - "System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Http;StreamContent;false;StreamContent;(System.IO.Stream);;Argument[0];Argument[Qualifier];taint;generated", "System.Net.Http;StreamContent;false;StreamContent;(System.IO.Stream,System.Int32);;Argument[0];Argument[Qualifier];taint;generated", - "System.Net.Http;StringContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", - "System.Net.Http;StringContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[2];ReturnValue;taint;generated", "System.Net.Http;StringContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated", - "System.Net.Http;StringContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Mail;AlternateView;false;CreateAlternateViewFromString;(System.String);;Argument[0];ReturnValue;taint;generated", "System.Net.Mail;AlternateView;false;CreateAlternateViewFromString;(System.String,System.Net.Mime.ContentType);;Argument[0];ReturnValue;taint;generated", "System.Net.Mail;AlternateView;false;CreateAlternateViewFromString;(System.String,System.Net.Mime.ContentType);;Argument[1];ReturnValue;taint;generated", @@ -5039,7 +4881,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Net.Quic;QuicListener;false;QuicListener;(System.Net.Quic.Implementations.QuicImplementationProvider,System.Net.Quic.QuicListenerOptions);;Argument[1];Argument[Qualifier];taint;generated", "System.Net.Quic;QuicListener;false;get_ListenEndPoint;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Security;AuthenticatedStream;false;AuthenticatedStream;(System.IO.Stream,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated", - "System.Net.Security;AuthenticatedStream;false;DisposeAsync;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Security;AuthenticatedStream;false;get_InnerStream;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Security;NegotiateStream;false;AuthenticateAsClient;(System.Net.NetworkCredential,System.Security.Authentication.ExtendedProtection.ChannelBinding,System.String);;Argument[1];Argument[Qualifier];taint;generated", "System.Net.Security;NegotiateStream;false;AuthenticateAsClient;(System.Net.NetworkCredential,System.Security.Authentication.ExtendedProtection.ChannelBinding,System.String);;Argument[2];Argument[Qualifier];taint;generated", @@ -5058,7 +4899,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Net.Security;NegotiateStream;false;AuthenticateAsServerAsync;(System.Net.NetworkCredential,System.Security.Authentication.ExtendedProtection.ExtendedProtectionPolicy,System.Net.Security.ProtectionLevel,System.Security.Principal.TokenImpersonationLevel);;Argument[1];Argument[Qualifier];taint;generated", "System.Net.Security;NegotiateStream;false;AuthenticateAsServerAsync;(System.Security.Authentication.ExtendedProtection.ExtendedProtectionPolicy);;Argument[0];Argument[Qualifier];taint;generated", "System.Net.Security;NegotiateStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", - "System.Net.Security;NegotiateStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Security;NegotiateStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", "System.Net.Security;NegotiateStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated", "System.Net.Security;NegotiateStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", @@ -5071,7 +4911,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Net.Security;SslCertificateTrust;false;CreateForX509Collection;(System.Security.Cryptography.X509Certificates.X509Certificate2Collection,System.Boolean);;Argument[0].Element;ReturnValue;taint;generated", "System.Net.Security;SslCertificateTrust;false;CreateForX509Store;(System.Security.Cryptography.X509Certificates.X509Store,System.Boolean);;Argument[0];ReturnValue;taint;generated", "System.Net.Security;SslStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", - "System.Net.Security;SslStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Security;SslStream;false;Write;(System.Byte[]);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.Net.Security;SslStream;false;get_LocalCertificate;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Security;SslStream;false;get_NegotiatedApplicationProtocol;();;Argument[Qualifier];ReturnValue;taint;generated", @@ -5102,7 +4941,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Net.Sockets;NetworkStream;false;get_Socket;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Sockets;SafeSocketHandle;false;SafeSocketHandle;(System.IntPtr,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated", "System.Net.Sockets;Socket;false;Accept;();;Argument[Qualifier];ReturnValue;taint;generated", - "System.Net.Sockets;Socket;false;AcceptAsync;(System.Net.Sockets.Socket);;Argument[0];ReturnValue;taint;generated", "System.Net.Sockets;Socket;false;AcceptAsync;(System.Net.Sockets.Socket,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", "System.Net.Sockets;Socket;false;AcceptAsync;(System.Net.Sockets.Socket,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated", "System.Net.Sockets;Socket;false;AcceptAsync;(System.Net.Sockets.Socket,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", @@ -5126,7 +4964,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Net.Sockets;Socket;false;DisconnectAsync;(System.Boolean,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated", "System.Net.Sockets;Socket;false;DisconnectAsync;(System.Boolean,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Sockets;Socket;false;DisconnectAsync;(System.Net.Sockets.SocketAsyncEventArgs);;Argument[Qualifier];Argument[0];taint;generated", - "System.Net.Sockets;Socket;false;EndAccept;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated", "System.Net.Sockets;Socket;false;ReceiveAsync;(System.Memory,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", "System.Net.Sockets;Socket;false;ReceiveAsync;(System.Memory,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken);;Argument[2];ReturnValue;taint;generated", "System.Net.Sockets;Socket;false;ReceiveAsync;(System.Memory,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", @@ -5146,8 +4983,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Net.Sockets;Socket;false;ReceiveFrom;(System.Span,System.Net.EndPoint);;Argument[1];ReturnValue;taint;generated", "System.Net.Sockets;Socket;false;ReceiveFrom;(System.Span,System.Net.Sockets.SocketFlags,System.Net.EndPoint);;Argument[2];Argument[Qualifier];taint;generated", "System.Net.Sockets;Socket;false;ReceiveFrom;(System.Span,System.Net.Sockets.SocketFlags,System.Net.EndPoint);;Argument[2];ReturnValue;taint;generated", - "System.Net.Sockets;Socket;false;ReceiveFromAsync;(System.ArraySegment,System.Net.EndPoint);;Argument[1];ReturnValue;taint;generated", - "System.Net.Sockets;Socket;false;ReceiveFromAsync;(System.ArraySegment,System.Net.Sockets.SocketFlags,System.Net.EndPoint);;Argument[2];ReturnValue;taint;generated", "System.Net.Sockets;Socket;false;ReceiveFromAsync;(System.Memory,System.Net.EndPoint,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", "System.Net.Sockets;Socket;false;ReceiveFromAsync;(System.Memory,System.Net.EndPoint,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated", "System.Net.Sockets;Socket;false;ReceiveFromAsync;(System.Memory,System.Net.EndPoint,System.Threading.CancellationToken);;Argument[2];ReturnValue;taint;generated", @@ -5161,8 +4996,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Net.Sockets;Socket;false;ReceiveMessageFrom;(System.Byte[],System.Int32,System.Int32,System.Net.Sockets.SocketFlags,System.Net.EndPoint,System.Net.Sockets.IPPacketInformation);;Argument[4];ReturnValue;taint;generated", "System.Net.Sockets;Socket;false;ReceiveMessageFrom;(System.Span,System.Net.Sockets.SocketFlags,System.Net.EndPoint,System.Net.Sockets.IPPacketInformation);;Argument[2];Argument[Qualifier];taint;generated", "System.Net.Sockets;Socket;false;ReceiveMessageFrom;(System.Span,System.Net.Sockets.SocketFlags,System.Net.EndPoint,System.Net.Sockets.IPPacketInformation);;Argument[2];ReturnValue;taint;generated", - "System.Net.Sockets;Socket;false;ReceiveMessageFromAsync;(System.ArraySegment,System.Net.EndPoint);;Argument[1];ReturnValue;taint;generated", - "System.Net.Sockets;Socket;false;ReceiveMessageFromAsync;(System.ArraySegment,System.Net.Sockets.SocketFlags,System.Net.EndPoint);;Argument[2];ReturnValue;taint;generated", "System.Net.Sockets;Socket;false;ReceiveMessageFromAsync;(System.Memory,System.Net.EndPoint,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", "System.Net.Sockets;Socket;false;ReceiveMessageFromAsync;(System.Memory,System.Net.EndPoint,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated", "System.Net.Sockets;Socket;false;ReceiveMessageFromAsync;(System.Memory,System.Net.EndPoint,System.Threading.CancellationToken);;Argument[2];ReturnValue;taint;generated", @@ -5221,7 +5054,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Net.Sockets;SocketAsyncEventArgs;false;set_SendPacketsElements;(System.Net.Sockets.SendPacketsElement[]);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.Net.Sockets;SocketAsyncEventArgs;false;set_UserToken;(System.Object);;Argument[0];Argument[Qualifier];taint;generated", "System.Net.Sockets;SocketException;false;get_Message;();;Argument[Qualifier];ReturnValue;taint;generated", - "System.Net.Sockets;SocketTaskExtensions;false;AcceptAsync;(System.Net.Sockets.Socket,System.Net.Sockets.Socket);;Argument[1];ReturnValue;taint;generated", "System.Net.Sockets;SocketTaskExtensions;false;ConnectAsync;(System.Net.Sockets.Socket,System.Net.EndPoint);;Argument[1];Argument[0];taint;generated", "System.Net.Sockets;SocketTaskExtensions;false;ConnectAsync;(System.Net.Sockets.Socket,System.Net.EndPoint,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", "System.Net.Sockets;SocketTaskExtensions;false;ConnectAsync;(System.Net.Sockets.Socket,System.Net.EndPoint,System.Threading.CancellationToken);;Argument[1];Argument[0];taint;generated", @@ -5233,8 +5065,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Net.Sockets;SocketTaskExtensions;false;ReceiveAsync;(System.Net.Sockets.Socket,System.Memory,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", "System.Net.Sockets;SocketTaskExtensions;false;ReceiveAsync;(System.Net.Sockets.Socket,System.Memory,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated", "System.Net.Sockets;SocketTaskExtensions;false;ReceiveAsync;(System.Net.Sockets.Socket,System.Memory,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken);;Argument[3];ReturnValue;taint;generated", - "System.Net.Sockets;SocketTaskExtensions;false;ReceiveFromAsync;(System.Net.Sockets.Socket,System.ArraySegment,System.Net.Sockets.SocketFlags,System.Net.EndPoint);;Argument[3];ReturnValue;taint;generated", - "System.Net.Sockets;SocketTaskExtensions;false;ReceiveMessageFromAsync;(System.Net.Sockets.Socket,System.ArraySegment,System.Net.Sockets.SocketFlags,System.Net.EndPoint);;Argument[3];ReturnValue;taint;generated", "System.Net.Sockets;SocketTaskExtensions;false;SendAsync;(System.Net.Sockets.Socket,System.ReadOnlyMemory,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", "System.Net.Sockets;SocketTaskExtensions;false;SendAsync;(System.Net.Sockets.Socket,System.ReadOnlyMemory,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken);;Argument[3];ReturnValue;taint;generated", "System.Net.Sockets;SocketTaskExtensions;false;SendToAsync;(System.Net.Sockets.Socket,System.ArraySegment,System.Net.Sockets.SocketFlags,System.Net.EndPoint);;Argument[3];Argument[0];taint;generated", @@ -5249,8 +5079,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Net.Sockets;TcpListener;false;AcceptSocketAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", "System.Net.Sockets;TcpListener;false;AcceptSocketAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Sockets;TcpListener;false;AcceptTcpClient;();;Argument[Qualifier];ReturnValue;taint;generated", - "System.Net.Sockets;TcpListener;false;EndAcceptSocket;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated", - "System.Net.Sockets;TcpListener;false;EndAcceptTcpClient;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated", "System.Net.Sockets;TcpListener;false;TcpListener;(System.Net.IPAddress,System.Int32);;Argument[0];Argument[Qualifier];taint;generated", "System.Net.Sockets;TcpListener;false;TcpListener;(System.Net.IPEndPoint);;Argument[0];Argument[Qualifier];taint;generated", "System.Net.Sockets;TcpListener;false;get_LocalEndpoint;();;Argument[Qualifier];ReturnValue;taint;generated", @@ -5335,17 +5163,11 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Net;CookieCollection;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated", "System.Net;CookieException;false;GetObjectData;(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext);;Argument[Qualifier];Argument[0];taint;generated", "System.Net;CredentialCache;false;GetCredential;(System.Uri,System.String);;Argument[Qualifier];ReturnValue;taint;generated", - "System.Net;Dns;false;EndGetHostAddresses;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated", - "System.Net;Dns;false;EndGetHostByName;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated", - "System.Net;Dns;false;EndGetHostEntry;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated", - "System.Net;Dns;false;EndResolve;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated", "System.Net;DnsEndPoint;false;DnsEndPoint;(System.String,System.Int32,System.Net.Sockets.AddressFamily);;Argument[0];Argument[Qualifier];taint;generated", "System.Net;DnsEndPoint;false;ToString;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net;DnsEndPoint;false;get_Host;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net;DownloadDataCompletedEventArgs;false;get_Result;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net;DownloadStringCompletedEventArgs;false;get_Result;();;Argument[Qualifier];ReturnValue;taint;generated", - "System.Net;FileWebRequest;false;EndGetRequestStream;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated", - "System.Net;FileWebRequest;false;EndGetResponse;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated", "System.Net;FileWebRequest;false;GetRequestStream;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net;FileWebRequest;false;GetResponse;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net;FileWebRequest;false;get_ContentType;();;Argument[Qualifier];ReturnValue;taint;generated", @@ -5419,9 +5241,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Net;HttpListenerTimeoutManager;false;get_IdleConnection;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net;HttpListenerTimeoutManager;false;set_DrainEntityBody;(System.TimeSpan);;Argument[0];Argument[Qualifier];taint;generated", "System.Net;HttpListenerTimeoutManager;false;set_IdleConnection;(System.TimeSpan);;Argument[0];Argument[Qualifier];taint;generated", - "System.Net;HttpWebRequest;false;EndGetRequestStream;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated", - "System.Net;HttpWebRequest;false;EndGetRequestStream;(System.IAsyncResult,System.Net.TransportContext);;Argument[0];ReturnValue;taint;generated", - "System.Net;HttpWebRequest;false;EndGetResponse;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated", "System.Net;HttpWebRequest;false;GetRequestStream;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net;HttpWebRequest;false;GetRequestStream;(System.Net.TransportContext);;Argument[Qualifier];ReturnValue;taint;generated", "System.Net;HttpWebRequest;false;GetResponse;();;Argument[Qualifier];ReturnValue;taint;generated", @@ -5478,7 +5297,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Net;OpenWriteCompletedEventArgs;false;get_Result;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net;PathList;false;get_Item;(System.String);;Argument[Qualifier];ReturnValue;taint;generated", "System.Net;PathList;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;taint;generated", - "System.Net;PathList;false;get_Values;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net;ProtocolViolationException;false;GetObjectData;(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext);;Argument[Qualifier];Argument[0];taint;generated", "System.Net;UploadDataCompletedEventArgs;false;get_Result;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net;UploadFileCompletedEventArgs;false;get_Result;();;Argument[Qualifier];ReturnValue;taint;generated", @@ -5508,8 +5326,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Net;WebClient;false;GetWebResponse;(System.Net.WebRequest);;Argument[0];ReturnValue;taint;generated", "System.Net;WebClient;false;GetWebResponse;(System.Net.WebRequest,System.IAsyncResult);;Argument[0];Argument[Qualifier];taint;generated", "System.Net;WebClient;false;GetWebResponse;(System.Net.WebRequest,System.IAsyncResult);;Argument[0];ReturnValue;taint;generated", - "System.Net;WebClient;false;GetWebResponse;(System.Net.WebRequest,System.IAsyncResult);;Argument[1];Argument[Qualifier];taint;generated", - "System.Net;WebClient;false;GetWebResponse;(System.Net.WebRequest,System.IAsyncResult);;Argument[1];ReturnValue;taint;generated", "System.Net;WebClient;false;OpenRead;(System.String);;Argument[0];Argument[Qualifier];taint;generated", "System.Net;WebClient;false;OpenRead;(System.String);;Argument[0];ReturnValue;taint;generated", "System.Net;WebClient;false;OpenRead;(System.String);;Argument[Qualifier];ReturnValue;taint;generated", @@ -6425,7 +6241,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Resources;ResourceReader;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Resources;ResourceReader;false;GetResourceData;(System.String,System.String,System.Byte[]);;Argument[Qualifier];ReturnValue;taint;generated", "System.Resources;ResourceReader;false;ResourceReader;(System.IO.Stream);;Argument[0];Argument[Qualifier];taint;generated", - "System.Resources;ResourceSet;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Resources;ResourceSet;false;ResourceSet;(System.IO.Stream);;Argument[0];Argument[Qualifier];taint;generated", "System.Resources;ResourceSet;false;ResourceSet;(System.Resources.IResourceReader);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.Resources;ResourceWriter;false;ResourceWriter;(System.IO.Stream);;Argument[0];Argument[Qualifier];taint;generated", @@ -6836,7 +6651,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Security.Cryptography.Pkcs;Pkcs12SafeBag;false;set_Attributes;(System.Security.Cryptography.CryptographicAttributeObjectCollection);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.Security.Cryptography.Pkcs;Pkcs12SafeContents;false;AddSafeBag;(System.Security.Cryptography.Pkcs.Pkcs12SafeBag);;Argument[0];Argument[Qualifier];taint;generated", "System.Security.Cryptography.Pkcs;Pkcs12SafeContents;false;AddSecret;(System.Security.Cryptography.Oid,System.ReadOnlyMemory);;Argument[0];ReturnValue;taint;generated", - "System.Security.Cryptography.Pkcs;Pkcs12SafeContents;false;GetBags;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Security.Cryptography.Pkcs;Pkcs12SecretBag;false;GetSecretType;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Security.Cryptography.Pkcs;Pkcs12SecretBag;false;get_SecretValue;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Security.Cryptography.Pkcs;Pkcs9AttributeObject;false;CopyFrom;(System.Security.Cryptography.AsnEncodedData);;Argument[0];Argument[Qualifier];taint;generated", @@ -6923,7 +6737,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Security.Cryptography.X509Certificates;X509Certificate;false;ToString;(System.Boolean);;Argument[Qualifier];ReturnValue;taint;generated", "System.Security.Cryptography.X509Certificates;X509Certificate;false;get_Issuer;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Security.Cryptography.X509Certificates;X509Certificate;false;get_Subject;();;Argument[Qualifier];ReturnValue;taint;generated", - "System.Security.Cryptography.X509Certificates;X509CertificateCollection+X509CertificateEnumerator;false;X509CertificateEnumerator;(System.Security.Cryptography.X509Certificates.X509CertificateCollection);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.Security.Cryptography.X509Certificates;X509CertificateCollection+X509CertificateEnumerator;false;get_Current;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Security.Cryptography.X509Certificates;X509CertificateCollection;false;Remove;(System.Security.Cryptography.X509Certificates.X509Certificate);;Argument[0];Argument[Qualifier];taint;generated", "System.Security.Cryptography.X509Certificates;X509CertificateCollection;false;X509CertificateCollection;(System.Security.Cryptography.X509Certificates.X509CertificateCollection);;Argument[0].Element;Argument[Qualifier];taint;generated", @@ -7044,7 +6857,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Security.Cryptography.Xml;EncryptionPropertyCollection;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Security.Cryptography.Xml;EncryptionPropertyCollection;false;set_ItemOf;(System.Int32,System.Security.Cryptography.Xml.EncryptionProperty);;Argument[1];Argument[Qualifier];taint;generated", "System.Security.Cryptography.Xml;KeyInfo;false;AddClause;(System.Security.Cryptography.Xml.KeyInfoClause);;Argument[0];Argument[Qualifier];taint;generated", - "System.Security.Cryptography.Xml;KeyInfo;false;GetEnumerator;(System.Type);;Argument[Qualifier];ReturnValue;taint;generated", "System.Security.Cryptography.Xml;KeyInfo;false;LoadXml;(System.Xml.XmlElement);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.Security.Cryptography.Xml;KeyInfo;false;get_Id;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Security.Cryptography.Xml;KeyInfo;false;set_Id;(System.String);;Argument[0];Argument[Qualifier];taint;generated", @@ -7157,7 +6969,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Security.Cryptography.Xml;Transform;false;set_Context;(System.Xml.XmlElement);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.Security.Cryptography.Xml;Transform;false;set_Resolver;(System.Xml.XmlResolver);;Argument[0];Argument[Qualifier];taint;generated", "System.Security.Cryptography.Xml;TransformChain;false;Add;(System.Security.Cryptography.Xml.Transform);;Argument[0];Argument[Qualifier];taint;generated", - "System.Security.Cryptography.Xml;TransformChain;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Security.Cryptography.Xml;TransformChain;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", "System.Security.Cryptography.Xml;XmlDecryptionTransform;false;AddExceptUri;(System.String);;Argument[0];Argument[Qualifier];taint;generated", "System.Security.Cryptography.Xml;XmlDecryptionTransform;false;GetOutput;();;Argument[Qualifier];ReturnValue;taint;generated", @@ -7223,7 +7034,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Security.Cryptography;CryptoStream;false;CryptoStream;(System.IO.Stream,System.Security.Cryptography.ICryptoTransform,System.Security.Cryptography.CryptoStreamMode,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated", "System.Security.Cryptography;CryptoStream;false;CryptoStream;(System.IO.Stream,System.Security.Cryptography.ICryptoTransform,System.Security.Cryptography.CryptoStreamMode,System.Boolean);;Argument[1];Argument[Qualifier];taint;generated", "System.Security.Cryptography;CryptoStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", - "System.Security.Cryptography;CryptoStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.Security.Cryptography;CryptographicAttributeObject;false;CryptographicAttributeObject;(System.Security.Cryptography.Oid,System.Security.Cryptography.AsnEncodedDataCollection);;Argument[0];Argument[Qualifier];taint;generated", "System.Security.Cryptography;CryptographicAttributeObject;false;get_Oid;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Security.Cryptography;CryptographicAttributeObjectCollection;false;Add;(System.Security.Cryptography.CryptographicAttributeObject);;Argument[0];Argument[Qualifier];taint;generated", @@ -7488,7 +7298,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Text.Encodings.Web;TextEncoder;true;Encode;(System.IO.TextWriter,System.String,System.Int32,System.Int32);;Argument[1];Argument[0];taint;generated", "System.Text.Encodings.Web;TextEncoder;true;Encode;(System.String);;Argument[0];ReturnValue;taint;generated", "System.Text.Json.Nodes;JsonArray;false;Add<>;(T);;Argument[0];Argument[Qualifier];taint;generated", - "System.Text.Json.Nodes;JsonArray;false;Add<>;(T);;Argument[Qualifier];Argument[0];taint;generated", "System.Text.Json.Nodes;JsonArray;false;Create;(System.Text.Json.JsonElement,System.Nullable);;Argument[0];ReturnValue;taint;generated", "System.Text.Json.Nodes;JsonArray;false;JsonArray;(System.Text.Json.Nodes.JsonNodeOptions,System.Text.Json.Nodes.JsonNode[]);;Argument[Qualifier];Argument[1].Element;taint;generated", "System.Text.Json.Nodes;JsonArray;false;JsonArray;(System.Text.Json.Nodes.JsonNode[]);;Argument[Qualifier];Argument[0].Element;taint;generated", @@ -7508,7 +7317,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Text.Json.Serialization;JsonSerializerContext;false;JsonSerializerContext;(System.Text.Json.JsonSerializerOptions);;Argument[Qualifier];Argument[0];taint;generated", "System.Text.Json.Serialization;JsonSerializerContext;false;get_Options;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Text.Json.Serialization;JsonStringEnumConverter;false;JsonStringEnumConverter;(System.Text.Json.JsonNamingPolicy,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated", - "System.Text.Json.SourceGeneration;JsonSourceGenerator;false;GetSerializableTypes;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Text.Json;JsonDocument;false;Parse;(System.Buffers.ReadOnlySequence,System.Text.Json.JsonDocumentOptions);;Argument[0];ReturnValue;taint;generated", "System.Text.Json;JsonDocument;false;Parse;(System.IO.Stream,System.Text.Json.JsonDocumentOptions);;Argument[0];ReturnValue;taint;generated", "System.Text.Json;JsonDocument;false;Parse;(System.ReadOnlyMemory,System.Text.Json.JsonDocumentOptions);;Argument[0];ReturnValue;taint;generated", @@ -7733,7 +7541,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Threading.RateLimiting;MetadataName<>;false;ToString;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Threading.RateLimiting;MetadataName<>;false;get_Name;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Threading.RateLimiting;RateLimitLease;false;TryGetMetadata<>;(System.Threading.RateLimiting.MetadataName,T);;Argument[Qualifier];ReturnValue;taint;generated", - "System.Threading.RateLimiting;RateLimitLease;true;GetAllMetadata;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Threading.RateLimiting;RateLimiter;false;Acquire;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", "System.Threading.RateLimiting;RateLimiter;false;WaitAsync;(System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.Threading.RateLimiting;TokenBucketRateLimiter;false;TokenBucketRateLimiter;(System.Threading.RateLimiting.TokenBucketRateLimiterOptions);;Argument[0];Argument[Qualifier];taint;generated", @@ -7775,10 +7582,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Threading.Tasks.Dataflow;DataflowBlock;false;Receive<>;(System.Threading.Tasks.Dataflow.ISourceBlock,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", "System.Threading.Tasks.Dataflow;DataflowBlock;false;Receive<>;(System.Threading.Tasks.Dataflow.ISourceBlock,System.TimeSpan);;Argument[0];ReturnValue;taint;generated", "System.Threading.Tasks.Dataflow;DataflowBlock;false;Receive<>;(System.Threading.Tasks.Dataflow.ISourceBlock,System.TimeSpan,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", - "System.Threading.Tasks.Dataflow;DataflowBlock;false;ReceiveAsync<>;(System.Threading.Tasks.Dataflow.ISourceBlock);;Argument[0];ReturnValue;taint;generated", - "System.Threading.Tasks.Dataflow;DataflowBlock;false;ReceiveAsync<>;(System.Threading.Tasks.Dataflow.ISourceBlock,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", - "System.Threading.Tasks.Dataflow;DataflowBlock;false;ReceiveAsync<>;(System.Threading.Tasks.Dataflow.ISourceBlock,System.TimeSpan);;Argument[0];ReturnValue;taint;generated", - "System.Threading.Tasks.Dataflow;DataflowBlock;false;ReceiveAsync<>;(System.Threading.Tasks.Dataflow.ISourceBlock,System.TimeSpan,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", "System.Threading.Tasks.Dataflow;DataflowBlock;false;SendAsync<>;(System.Threading.Tasks.Dataflow.ITargetBlock,TInput,System.Threading.CancellationToken);;Argument[1];Argument[0];taint;generated", "System.Threading.Tasks.Dataflow;DataflowBlock;false;TryReceive<>;(System.Threading.Tasks.Dataflow.IReceivableSourceBlock,TOutput);;Argument[0];ReturnValue;taint;generated", "System.Threading.Tasks.Dataflow;DataflowBlockOptions;false;get_CancellationToken;();;Argument[Qualifier];ReturnValue;taint;generated", @@ -8374,12 +8177,9 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Xml.Schema;XmlSchemaSet;false;Add;(System.String,System.Xml.XmlReader);;Argument[Qualifier];ReturnValue;taint;generated", "System.Xml.Schema;XmlSchemaSet;false;Add;(System.Xml.Schema.XmlSchema);;Argument[0];Argument[Qualifier];taint;generated", "System.Xml.Schema;XmlSchemaSet;false;Add;(System.Xml.Schema.XmlSchema);;Argument[0];ReturnValue;taint;generated", - "System.Xml.Schema;XmlSchemaSet;false;Add;(System.Xml.Schema.XmlSchemaSet);;Argument[0];Argument[Qualifier];taint;generated", - "System.Xml.Schema;XmlSchemaSet;false;CopyTo;(System.Xml.Schema.XmlSchema[],System.Int32);;Argument[Qualifier];Argument[0].Element;taint;generated", "System.Xml.Schema;XmlSchemaSet;false;Remove;(System.Xml.Schema.XmlSchema);;Argument[0];ReturnValue;taint;generated", "System.Xml.Schema;XmlSchemaSet;false;Reprocess;(System.Xml.Schema.XmlSchema);;Argument[0];Argument[Qualifier];taint;generated", "System.Xml.Schema;XmlSchemaSet;false;Reprocess;(System.Xml.Schema.XmlSchema);;Argument[0];ReturnValue;taint;generated", - "System.Xml.Schema;XmlSchemaSet;false;Schemas;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Xml.Schema;XmlSchemaSet;false;XmlSchemaSet;(System.Xml.XmlNameTable);;Argument[0];Argument[Qualifier];taint;generated", "System.Xml.Schema;XmlSchemaSet;false;get_CompilationSettings;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Xml.Schema;XmlSchemaSet;false;get_GlobalAttributes;();;Argument[Qualifier];ReturnValue;taint;generated", @@ -8466,10 +8266,8 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Xml.Schema;XmlSchemaXPath;false;get_XPath;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Xml.Schema;XmlSchemaXPath;false;set_XPath;(System.String);;Argument[0];Argument[Qualifier];taint;generated", "System.Xml.Serialization;CodeIdentifiers;false;Add;(System.String,System.Object);;Argument[1];Argument[Qualifier];taint;generated", - "System.Xml.Serialization;CodeIdentifiers;false;Add;(System.String,System.Object);;Argument[Qualifier];Argument[1];taint;generated", "System.Xml.Serialization;CodeIdentifiers;false;AddUnique;(System.String,System.Object);;Argument[0];ReturnValue;taint;generated", "System.Xml.Serialization;CodeIdentifiers;false;AddUnique;(System.String,System.Object);;Argument[1];Argument[Qualifier];taint;generated", - "System.Xml.Serialization;CodeIdentifiers;false;AddUnique;(System.String,System.Object);;Argument[Qualifier];Argument[1];taint;generated", "System.Xml.Serialization;CodeIdentifiers;false;MakeUnique;(System.String);;Argument[0];ReturnValue;taint;generated", "System.Xml.Serialization;CodeIdentifiers;false;ToArray;(System.Type);;Argument[Qualifier];ReturnValue;taint;generated", "System.Xml.Serialization;ImportContext;false;ImportContext;(System.Xml.Serialization.CodeIdentifiers,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated", @@ -8752,7 +8550,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Xml.Serialization;XmlSerializationWriter;false;WriteElementStringRaw;(System.String,System.String,System.String);;Argument[2];Argument[Qualifier];taint;generated", "System.Xml.Serialization;XmlSerializationWriter;false;WriteElementStringRaw;(System.String,System.String,System.String,System.Xml.XmlQualifiedName);;Argument[2];Argument[Qualifier];taint;generated", "System.Xml.Serialization;XmlSerializationWriter;false;WriteElementStringRaw;(System.String,System.String,System.Xml.XmlQualifiedName);;Argument[1];Argument[Qualifier];taint;generated", - "System.Xml.Serialization;XmlSerializationWriter;false;WriteId;(System.Object);;Argument[Qualifier];Argument[0];taint;generated", "System.Xml.Serialization;XmlSerializationWriter;false;WriteNullableStringEncoded;(System.String,System.String,System.String,System.Xml.XmlQualifiedName);;Argument[2];Argument[Qualifier];taint;generated", "System.Xml.Serialization;XmlSerializationWriter;false;WriteNullableStringEncodedRaw;(System.String,System.String,System.Byte[],System.Xml.XmlQualifiedName);;Argument[2].Element;Argument[Qualifier];taint;generated", "System.Xml.Serialization;XmlSerializationWriter;false;WriteNullableStringEncodedRaw;(System.String,System.String,System.String,System.Xml.XmlQualifiedName);;Argument[2];Argument[Qualifier];taint;generated", @@ -8760,15 +8557,9 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Xml.Serialization;XmlSerializationWriter;false;WriteNullableStringLiteralRaw;(System.String,System.String,System.Byte[]);;Argument[2].Element;Argument[Qualifier];taint;generated", "System.Xml.Serialization;XmlSerializationWriter;false;WriteNullableStringLiteralRaw;(System.String,System.String,System.String);;Argument[2];Argument[Qualifier];taint;generated", "System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object);;Argument[2];Argument[Qualifier];taint;generated", - "System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object);;Argument[Qualifier];Argument[2];taint;generated", "System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object,System.Type);;Argument[2];Argument[Qualifier];taint;generated", - "System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object,System.Type);;Argument[Qualifier];Argument[2];taint;generated", "System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object,System.Type,System.Boolean);;Argument[2];Argument[Qualifier];taint;generated", - "System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object,System.Type,System.Boolean);;Argument[Qualifier];Argument[2];taint;generated", "System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object,System.Type,System.Boolean,System.Boolean);;Argument[2];Argument[Qualifier];taint;generated", - "System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object,System.Type,System.Boolean,System.Boolean);;Argument[Qualifier];Argument[2];taint;generated", - "System.Xml.Serialization;XmlSerializationWriter;false;WriteReferencingElement;(System.String,System.String,System.Object);;Argument[Qualifier];Argument[2];taint;generated", - "System.Xml.Serialization;XmlSerializationWriter;false;WriteReferencingElement;(System.String,System.String,System.Object,System.Boolean);;Argument[Qualifier];Argument[2];taint;generated", "System.Xml.Serialization;XmlSerializationWriter;false;WriteRpcResult;(System.String,System.String);;Argument[0];Argument[Qualifier];taint;generated", "System.Xml.Serialization;XmlSerializationWriter;false;WriteRpcResult;(System.String,System.String);;Argument[1];Argument[Qualifier];taint;generated", "System.Xml.Serialization;XmlSerializationWriter;false;WriteSerializable;(System.Xml.Serialization.IXmlSerializable,System.String,System.String,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated", @@ -9945,27 +9736,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System;Tuple<,>;false;get_Item2;();;Argument[Qualifier];ReturnValue;taint;generated", "System;Tuple<>;false;ToString;();;Argument[Qualifier];ReturnValue;taint;generated", "System;Tuple<>;false;get_Item1;();;Argument[Qualifier];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<,,,,,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<,,,,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<,,,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<,,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<,,,,,,>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<,,,,,>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<,,,,>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<,,,>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<,,>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<,>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated", "System;TupleExtensions;false;ToValueTuple<,,,,,,,,,,,,,,,,,,,,>;(System.Tuple>>);;Argument[0];ReturnValue;taint;generated", "System;TupleExtensions;false;ToValueTuple<,,,,,,,,,,,,,,,,,,,>;(System.Tuple>>);;Argument[0];ReturnValue;taint;generated", "System;TupleExtensions;false;ToValueTuple<,,,,,,,,,,,,,,,,,,>;(System.Tuple>>);;Argument[0];ReturnValue;taint;generated", From 8899bf7f059ad0630366c53557419af1b0cf5084 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Wed, 22 Jun 2022 13:03:23 +0200 Subject: [PATCH 079/505] C#: Update tests. --- .../dataflow/library/FlowSummaries.expected | 241 ------------------ .../library/FlowSummariesFiltered.expected | 170 +----------- 2 files changed, 8 insertions(+), 403 deletions(-) diff --git a/csharp/ql/test/library-tests/dataflow/library/FlowSummaries.expected b/csharp/ql/test/library-tests/dataflow/library/FlowSummaries.expected index b6d827ca880..2d9fce246fb 100644 --- a/csharp/ql/test/library-tests/dataflow/library/FlowSummaries.expected +++ b/csharp/ql/test/library-tests/dataflow/library/FlowSummaries.expected @@ -336,7 +336,6 @@ | System.Collections.Concurrent;ConcurrentDictionary<,>;false;CopyTo;(System.Collections.Generic.KeyValuePair[],System.Int32);;Argument[Qualifier].Element;Argument[0].Element;value;manual | | System.Collections.Concurrent;ConcurrentDictionary<,>;false;GetEnumerator;();;Argument[Qualifier].Element;ReturnValue.Property[System.Collections.Generic.IEnumerator<>.Current];value;manual | | System.Collections.Concurrent;ConcurrentDictionary<,>;false;GetEnumerator;();;Argument[Qualifier].Element;ReturnValue.Property[System.Collections.IEnumerator.Current];value;manual | -| System.Collections.Concurrent;ConcurrentDictionary<,>;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Concurrent;ConcurrentDictionary<,>;false;GetOrAdd;(TKey,TValue);;Argument[1];ReturnValue;taint;generated | | System.Collections.Concurrent;ConcurrentDictionary<,>;false;get_Item;(System.Object);;Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];ReturnValue;value;manual | | System.Collections.Concurrent;ConcurrentDictionary<,>;false;get_Item;(TKey);;Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];ReturnValue;value;manual | @@ -370,9 +369,6 @@ | System.Collections.Generic;CollectionExtensions;false;GetValueOrDefault<,>;(System.Collections.Generic.IReadOnlyDictionary,TKey,TValue);;Argument[1];ReturnValue;taint;generated | | System.Collections.Generic;CollectionExtensions;false;GetValueOrDefault<,>;(System.Collections.Generic.IReadOnlyDictionary,TKey,TValue);;Argument[2];ReturnValue;taint;generated | | System.Collections.Generic;CollectionExtensions;false;Remove<,>;(System.Collections.Generic.IDictionary,TKey,TValue);;Argument[0].Element;ReturnValue;taint;generated | -| System.Collections.Generic;CollectionExtensions;false;TryAdd<,>;(System.Collections.Generic.IDictionary,TKey,TValue);;Argument[0].Element;Argument[2];taint;generated | -| System.Collections.Generic;CollectionExtensions;false;TryAdd<,>;(System.Collections.Generic.IDictionary,TKey,TValue);;Argument[1];Argument[0].Element;taint;generated | -| System.Collections.Generic;CollectionExtensions;false;TryAdd<,>;(System.Collections.Generic.IDictionary,TKey,TValue);;Argument[2];Argument[0].Element;taint;generated | | System.Collections.Generic;Dictionary<,>+Enumerator;false;get_Current;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Generic;Dictionary<,>+Enumerator;false;get_Entry;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Generic;Dictionary<,>+Enumerator;false;get_Key;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -451,8 +447,6 @@ | System.Collections.Generic;IList<>;true;get_Item;(System.Int32);;Argument[Qualifier].Element;ReturnValue;value;manual | | System.Collections.Generic;IList<>;true;set_Item;(System.Int32,T);;Argument[1];Argument[Qualifier].Element;value;manual | | System.Collections.Generic;ISet<>;true;Add;(T);;Argument[0];Argument[Qualifier].Element;value;manual | -| System.Collections.Generic;KeyValuePair;false;Create<,>;(TKey,TValue);;Argument[0];ReturnValue;taint;generated | -| System.Collections.Generic;KeyValuePair;false;Create<,>;(TKey,TValue);;Argument[1];ReturnValue;taint;generated | | System.Collections.Generic;KeyValuePair<,>;false;Deconstruct;(TKey,TValue);;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Generic;KeyValuePair<,>;false;KeyValuePair;(TKey,TValue);;Argument[0];ReturnValue.Property[System.Collections.Generic.KeyValuePair<,>.Key];value;manual | | System.Collections.Generic;KeyValuePair<,>;false;KeyValuePair;(TKey,TValue);;Argument[1];ReturnValue.Property[System.Collections.Generic.KeyValuePair<,>.Value];value;manual | @@ -726,12 +720,6 @@ | System.Collections.Immutable;ImmutableDictionary;false;Create<,>;(System.Collections.Generic.IEqualityComparer);;Argument[0];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableDictionary;false;Create<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEqualityComparer);;Argument[0];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableDictionary;false;Create<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEqualityComparer);;Argument[1];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEnumerable>);;Argument[0].Element;ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[0];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[1].Element;ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[0];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[1];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[2].Element;ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableDictionary;false;GetValueOrDefault<,>;(System.Collections.Immutable.IImmutableDictionary,TKey,TValue);;Argument[2];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableDictionary;false;ToImmutableDictionary<,>;(System.Collections.Generic.IEnumerable>);;Argument[0].Element;ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableDictionary;false;ToImmutableDictionary<,>;(System.Collections.Generic.IEnumerable>,System.Collections.Generic.IEqualityComparer);;Argument[0].Element;ReturnValue;taint;generated | @@ -802,8 +790,6 @@ | System.Collections.Immutable;ImmutableDictionary<,>;false;set_Item;(System.Object,System.Object);;Argument[1];Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];value;manual | | System.Collections.Immutable;ImmutableDictionary<,>;false;set_Item;(TKey,TValue);;Argument[0];Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Key];value;manual | | System.Collections.Immutable;ImmutableDictionary<,>;false;set_Item;(TKey,TValue);;Argument[1];Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];value;manual | -| System.Collections.Immutable;ImmutableHashSet;false;Create<>;(System.Collections.Generic.IEqualityComparer,T);;Argument[1];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableHashSet;false;Create<>;(T);;Argument[0];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableHashSet;false;CreateRange<>;(System.Collections.Generic.IEnumerable);;Argument[0].Element;ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableHashSet;false;CreateRange<>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable);;Argument[1].Element;ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableHashSet;false;ToImmutableHashSet<>;(System.Collections.Generic.IEnumerable);;Argument[0].Element;ReturnValue;taint;generated | @@ -836,9 +822,6 @@ | System.Collections.Immutable;ImmutableHashSet<>;false;get_KeyComparer;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableHashSet<>;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated | | System.Collections.Immutable;ImmutableInterlocked;false;GetOrAdd<,>;(System.Collections.Immutable.ImmutableDictionary,TKey,TValue);;Argument[2];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableList;false;Create<>;(T);;Argument[0];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableList;false;Create<>;(T[]);;Argument[0].Element;ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableList;false;CreateRange<>;(System.Collections.Generic.IEnumerable);;Argument[0].Element;ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableList;false;Remove<>;(System.Collections.Immutable.IImmutableList,T);;Argument[0].Element;ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableList;false;RemoveRange<>;(System.Collections.Immutable.IImmutableList,System.Collections.Generic.IEnumerable);;Argument[0].Element;ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableList;false;Replace<>;(System.Collections.Immutable.IImmutableList,T,T);;Argument[0].Element;ReturnValue;taint;generated | @@ -945,12 +928,6 @@ | System.Collections.Immutable;ImmutableSortedDictionary;false;CreateBuilder<,>;(System.Collections.Generic.IComparer);;Argument[0];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedDictionary;false;CreateBuilder<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEqualityComparer);;Argument[0];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedDictionary;false;CreateBuilder<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEqualityComparer);;Argument[1];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEnumerable>);;Argument[0];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEnumerable>);;Argument[1].Element;ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[0];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[1];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[2].Element;ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IEnumerable>);;Argument[0].Element;ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedDictionary;false;ToImmutableSortedDictionary<,>;(System.Collections.Generic.IEnumerable>);;Argument[0].Element;ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedDictionary;false;ToImmutableSortedDictionary<,>;(System.Collections.Generic.IEnumerable>,System.Collections.Generic.IComparer);;Argument[0].Element;ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedDictionary;false;ToImmutableSortedDictionary<,>;(System.Collections.Generic.IEnumerable>,System.Collections.Generic.IComparer);;Argument[1];ReturnValue;taint;generated | @@ -988,7 +965,6 @@ | System.Collections.Immutable;ImmutableSortedDictionary<,>+Builder;false;set_Item;(TKey,TValue);;Argument[1];Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];value;manual | | System.Collections.Immutable;ImmutableSortedDictionary<,>+Builder;false;set_KeyComparer;(System.Collections.Generic.IComparer);;Argument[0];Argument[Qualifier];taint;generated | | System.Collections.Immutable;ImmutableSortedDictionary<,>+Builder;false;set_ValueComparer;(System.Collections.Generic.IEqualityComparer);;Argument[0];Argument[Qualifier];taint;generated | -| System.Collections.Immutable;ImmutableSortedDictionary<,>+Enumerator;false;get_Current;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedDictionary<,>;false;Add;(System.Collections.Generic.KeyValuePair);;Argument[0].Property[System.Collections.Generic.KeyValuePair<,>.Key];Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Key];value;manual | | System.Collections.Immutable;ImmutableSortedDictionary<,>;false;Add;(System.Collections.Generic.KeyValuePair);;Argument[0].Property[System.Collections.Generic.KeyValuePair<,>.Value];Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];value;manual | | System.Collections.Immutable;ImmutableSortedDictionary<,>;false;Add;(System.Collections.Generic.KeyValuePair);;Argument[0];Argument[Qualifier].Element;value;manual | @@ -1027,10 +1003,7 @@ | System.Collections.Immutable;ImmutableSortedDictionary<,>;false;set_Item;(TKey,TValue);;Argument[0];Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Key];value;manual | | System.Collections.Immutable;ImmutableSortedDictionary<,>;false;set_Item;(TKey,TValue);;Argument[1];Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];value;manual | | System.Collections.Immutable;ImmutableSortedSet;false;Create<>;(System.Collections.Generic.IComparer);;Argument[0];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableSortedSet;false;Create<>;(System.Collections.Generic.IComparer,T);;Argument[0];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableSortedSet;false;Create<>;(System.Collections.Generic.IComparer,T);;Argument[1];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet;false;Create<>;(System.Collections.Generic.IComparer,T[]);;Argument[0];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableSortedSet;false;Create<>;(T);;Argument[0];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet;false;CreateBuilder<>;(System.Collections.Generic.IComparer);;Argument[0];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet;false;CreateRange<>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEnumerable);;Argument[0];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet;false;CreateRange<>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEnumerable);;Argument[1].Element;ReturnValue;taint;generated | @@ -1047,7 +1020,6 @@ | System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;GetEnumerator;();;Argument[Qualifier].Element;ReturnValue.Property[System.Collections.Immutable.ImmutableSortedSet<>+Enumerator.Current];value;manual | | System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;IntersectWith;(System.Collections.Generic.IEnumerable);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;Reverse;();;Argument[0].Element;ReturnValue.Element;value;manual | -| System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;SymmetricExceptWith;(System.Collections.Generic.IEnumerable);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;ToImmutable;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;TryGetValue;(T,T);;Argument[0];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;TryGetValue;(T,T);;Argument[Qualifier];ReturnValue;taint;generated | @@ -1068,12 +1040,8 @@ | System.Collections.Immutable;ImmutableSortedSet<>;false;GetEnumerator;();;Argument[Qualifier].Element;ReturnValue.Property[System.Collections.Immutable.ImmutableSortedSet<>+Enumerator.Current];value;manual | | System.Collections.Immutable;ImmutableSortedSet<>;false;Insert;(System.Int32,System.Object);;Argument[1];Argument[Qualifier].Element;value;manual | | System.Collections.Immutable;ImmutableSortedSet<>;false;Insert;(System.Int32,T);;Argument[1];Argument[Qualifier].Element;value;manual | -| System.Collections.Immutable;ImmutableSortedSet<>;false;Intersect;(System.Collections.Generic.IEnumerable);;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet<>;false;Remove;(T);;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet<>;false;Reverse;();;Argument[0].Element;ReturnValue.Element;value;manual | -| System.Collections.Immutable;ImmutableSortedSet<>;false;SymmetricExcept;(System.Collections.Generic.IEnumerable);;Argument[0].Element;Argument[Qualifier];taint;generated | -| System.Collections.Immutable;ImmutableSortedSet<>;false;SymmetricExcept;(System.Collections.Generic.IEnumerable);;Argument[0].Element;ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableSortedSet<>;false;SymmetricExcept;(System.Collections.Generic.IEnumerable);;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet<>;false;ToBuilder;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet<>;false;TryGetValue;(T,T);;Argument[0];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet<>;false;TryGetValue;(T,T);;Argument[Qualifier];ReturnValue;taint;generated | @@ -1111,27 +1079,21 @@ | System.Collections.ObjectModel;Collection<>;false;Insert;(System.Int32,System.Object);;Argument[1];Argument[Qualifier].Element;value;manual | | System.Collections.ObjectModel;Collection<>;false;Insert;(System.Int32,T);;Argument[1];Argument[Qualifier].Element;value;manual | | System.Collections.ObjectModel;Collection<>;false;InsertItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated | -| System.Collections.ObjectModel;Collection<>;false;InsertItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated | | System.Collections.ObjectModel;Collection<>;false;SetItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated | -| System.Collections.ObjectModel;Collection<>;false;SetItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated | | System.Collections.ObjectModel;Collection<>;false;get_Item;(System.Int32);;Argument[Qualifier].Element;ReturnValue;value;manual | | System.Collections.ObjectModel;Collection<>;false;get_Items;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.ObjectModel;Collection<>;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.ObjectModel;Collection<>;false;set_Item;(System.Int32,System.Object);;Argument[1];Argument[Qualifier].Element;value;manual | | System.Collections.ObjectModel;Collection<>;false;set_Item;(System.Int32,T);;Argument[1];Argument[Qualifier].Element;value;manual | | System.Collections.ObjectModel;KeyedCollection<,>;false;InsertItem;(System.Int32,TItem);;Argument[1];Argument[Qualifier];taint;generated | -| System.Collections.ObjectModel;KeyedCollection<,>;false;InsertItem;(System.Int32,TItem);;Argument[Qualifier];Argument[1];taint;generated | | System.Collections.ObjectModel;KeyedCollection<,>;false;KeyedCollection;(System.Collections.Generic.IEqualityComparer,System.Int32);;Argument[0];Argument[Qualifier];taint;generated | | System.Collections.ObjectModel;KeyedCollection<,>;false;SetItem;(System.Int32,TItem);;Argument[1];Argument[Qualifier];taint;generated | -| System.Collections.ObjectModel;KeyedCollection<,>;false;SetItem;(System.Int32,TItem);;Argument[Qualifier];Argument[1];taint;generated | | System.Collections.ObjectModel;KeyedCollection<,>;false;TryGetValue;(TKey,TItem);;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.ObjectModel;KeyedCollection<,>;false;get_Comparer;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.ObjectModel;KeyedCollection<,>;false;get_Dictionary;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.ObjectModel;KeyedCollection<,>;false;get_Item;(TKey);;Argument[Qualifier].Element;ReturnValue;value;manual | | System.Collections.ObjectModel;ObservableCollection<>;false;InsertItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated | -| System.Collections.ObjectModel;ObservableCollection<>;false;InsertItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated | | System.Collections.ObjectModel;ObservableCollection<>;false;SetItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated | -| System.Collections.ObjectModel;ObservableCollection<>;false;SetItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated | | System.Collections.ObjectModel;ReadOnlyCollection<>;false;Add;(System.Object);;Argument[0];Argument[Qualifier].Element;value;manual | | System.Collections.ObjectModel;ReadOnlyCollection<>;false;Add;(T);;Argument[0];Argument[Qualifier].Element;value;manual | | System.Collections.ObjectModel;ReadOnlyCollection<>;false;CopyTo;(System.Array,System.Int32);;Argument[Qualifier].Element;Argument[0].Element;value;manual | @@ -1328,7 +1290,6 @@ | System.Collections;CollectionBase;false;GetEnumerator;();;Argument[Qualifier].Element;ReturnValue.Property[System.Collections.IEnumerator.Current];value;manual | | System.Collections;CollectionBase;false;Insert;(System.Int32,System.Object);;Argument[1];Argument[Qualifier].Element;value;manual | | System.Collections;CollectionBase;false;Remove;(System.Object);;Argument[0];Argument[Qualifier];taint;generated | -| System.Collections;CollectionBase;false;Remove;(System.Object);;Argument[Qualifier];Argument[0];taint;generated | | System.Collections;CollectionBase;false;get_InnerList;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections;CollectionBase;false;get_Item;(System.Int32);;Argument[Qualifier].Element;ReturnValue;value;manual | | System.Collections;CollectionBase;false;get_List;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -1559,9 +1520,7 @@ | System.ComponentModel;BaseNumberConverter;false;ConvertTo;(System.ComponentModel.ITypeDescriptorContext,System.Globalization.CultureInfo,System.Object,System.Type);;Argument[2];ReturnValue;taint;generated | | System.ComponentModel;BindingList<>;false;Find;(System.ComponentModel.PropertyDescriptor,System.Object);;Argument[Qualifier].Element;ReturnValue;value;manual | | System.ComponentModel;BindingList<>;false;InsertItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated | -| System.ComponentModel;BindingList<>;false;InsertItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated | | System.ComponentModel;BindingList<>;false;SetItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated | -| System.ComponentModel;BindingList<>;false;SetItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated | | System.ComponentModel;CategoryAttribute;false;CategoryAttribute;(System.String);;Argument[0];Argument[Qualifier];taint;generated | | System.ComponentModel;CategoryAttribute;false;get_Category;();;Argument[Qualifier];ReturnValue;taint;generated | | System.ComponentModel;CharConverter;false;ConvertTo;(System.ComponentModel.ITypeDescriptorContext,System.Globalization.CultureInfo,System.Object,System.Type);;Argument[2];ReturnValue;taint;generated | @@ -1882,10 +1841,6 @@ | System.Data.Common;DataTableMappingCollection;false;set_Item;(System.String,System.Object);;Argument[1];Argument[Qualifier].Element;value;manual | | System.Data.Common;DbCommand;false;ExecuteReader;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbCommand;false;ExecuteReader;(System.Data.CommandBehavior);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Data.Common;DbCommand;false;ExecuteReaderAsync;();;Argument[Qualifier];ReturnValue;taint;generated | -| System.Data.Common;DbCommand;false;ExecuteReaderAsync;(System.Data.CommandBehavior);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Data.Common;DbCommand;false;ExecuteReaderAsync;(System.Data.CommandBehavior,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Data.Common;DbCommand;false;ExecuteReaderAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbCommand;false;get_Connection;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbCommand;false;get_Parameters;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbCommand;false;get_Transaction;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -1893,7 +1848,6 @@ | System.Data.Common;DbCommand;false;set_Connection;(System.Data.IDbConnection);;Argument[0];Argument[Qualifier];taint;generated | | System.Data.Common;DbCommand;false;set_Transaction;(System.Data.Common.DbTransaction);;Argument[0];Argument[Qualifier];taint;generated | | System.Data.Common;DbCommand;false;set_Transaction;(System.Data.IDbTransaction);;Argument[0];Argument[Qualifier];taint;generated | -| System.Data.Common;DbCommand;true;ExecuteDbDataReaderAsync;(System.Data.CommandBehavior,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbCommand;true;PrepareAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Data.Common;DbCommandBuilder;false;GetDeleteCommand;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbCommandBuilder;false;GetDeleteCommand;(System.Boolean);;Argument[Qualifier];ReturnValue;taint;generated | @@ -1926,7 +1880,6 @@ | System.Data.Common;DbConnectionStringBuilder;false;AppendKeyValuePair;(System.Text.StringBuilder,System.String,System.String,System.Boolean);;Argument[2];Argument[0];taint;generated | | System.Data.Common;DbConnectionStringBuilder;false;CopyTo;(System.Array,System.Int32);;Argument[Qualifier].Element;Argument[0].Element;value;manual | | System.Data.Common;DbConnectionStringBuilder;false;GetEnumerator;();;Argument[Qualifier].Element;ReturnValue.Property[System.Collections.IEnumerator.Current];value;manual | -| System.Data.Common;DbConnectionStringBuilder;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbConnectionStringBuilder;false;GetProperties;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbConnectionStringBuilder;false;GetProperties;(System.Attribute[]);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbConnectionStringBuilder;false;GetPropertyOwner;(System.ComponentModel.PropertyDescriptor);;Argument[Qualifier];ReturnValue;value;generated | @@ -1961,13 +1914,10 @@ | System.Data.Common;DbDataAdapter;true;CreateRowUpdatingEvent;(System.Data.DataRow,System.Data.IDbCommand,System.Data.StatementType,System.Data.Common.DataTableMapping);;Argument[0];ReturnValue;taint;generated | | System.Data.Common;DbDataAdapter;true;CreateRowUpdatingEvent;(System.Data.DataRow,System.Data.IDbCommand,System.Data.StatementType,System.Data.Common.DataTableMapping);;Argument[1];ReturnValue;taint;generated | | System.Data.Common;DbDataAdapter;true;CreateRowUpdatingEvent;(System.Data.DataRow,System.Data.IDbCommand,System.Data.StatementType,System.Data.Common.DataTableMapping);;Argument[3];ReturnValue;taint;generated | -| System.Data.Common;DbDataReader;false;GetFieldValueAsync<>;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbDataReader;true;GetEnumerator;();;Argument[Qualifier].Element;ReturnValue.Property[System.Collections.IEnumerator.Current];value;manual | | System.Data.Common;DbDataReader;true;GetFieldValue<>;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Data.Common;DbDataReader;true;GetFieldValueAsync<>;(System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbDataReader;true;GetProviderSpecificValue;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbDataReader;true;GetProviderSpecificValues;(System.Object[]);;Argument[Qualifier];Argument[0].Element;taint;generated | -| System.Data.Common;DbDataReader;true;GetSchemaTableAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbDataReader;true;GetTextReader;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbDataRecord;false;GetPropertyOwner;(System.ComponentModel.PropertyDescriptor);;Argument[Qualifier];ReturnValue;value;generated | | System.Data.Common;DbEnumerator;false;DbEnumerator;(System.Data.IDataReader);;Argument[0];Argument[Qualifier];taint;generated | @@ -2108,11 +2058,8 @@ | System.Data;DataColumn;false;set_Prefix;(System.String);;Argument[0];Argument[Qualifier];taint;generated | | System.Data;DataColumnChangeEventArgs;false;DataColumnChangeEventArgs;(System.Data.DataRow,System.Data.DataColumn,System.Object);;Argument[1];Argument[Qualifier];taint;generated | | System.Data;DataColumnChangeEventArgs;false;get_Column;();;Argument[Qualifier];ReturnValue;taint;generated | -| System.Data;DataColumnCollection;false;Add;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Data;DataColumnCollection;false;Add;(System.Data.DataColumn);;Argument[0];Argument[Qualifier].Element;value;manual | | System.Data;DataColumnCollection;false;Add;(System.String);;Argument[0];Argument[Qualifier].Element;value;manual | -| System.Data;DataColumnCollection;false;Add;(System.String,System.Type);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Data;DataColumnCollection;false;Add;(System.String,System.Type,System.String);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data;DataColumnCollection;false;AddRange;(System.Data.DataColumn[]);;Argument[0].Element;Argument[Qualifier].Element;value;manual | | System.Data;DataColumnCollection;false;CopyTo;(System.Data.DataColumn[],System.Int32);;Argument[Qualifier].Element;Argument[0].Element;value;manual | | System.Data;DataColumnCollection;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | @@ -2120,7 +2067,6 @@ | System.Data;DataColumnCollection;false;get_List;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Data;DataReaderExtensions;false;GetDateTime;(System.Data.Common.DbDataReader,System.String);;Argument[0].Element;ReturnValue;taint;generated | | System.Data;DataReaderExtensions;false;GetFieldValue<>;(System.Data.Common.DbDataReader,System.String);;Argument[0].Element;ReturnValue;taint;generated | -| System.Data;DataReaderExtensions;false;GetFieldValueAsync<>;(System.Data.Common.DbDataReader,System.String,System.Threading.CancellationToken);;Argument[0].Element;ReturnValue;taint;generated | | System.Data;DataReaderExtensions;false;GetGuid;(System.Data.Common.DbDataReader,System.String);;Argument[0].Element;ReturnValue;taint;generated | | System.Data;DataReaderExtensions;false;GetProviderSpecificValue;(System.Data.Common.DbDataReader,System.String);;Argument[0].Element;ReturnValue;taint;generated | | System.Data;DataReaderExtensions;false;GetString;(System.Data.Common.DbDataReader,System.String);;Argument[0].Element;ReturnValue;taint;generated | @@ -2152,16 +2098,10 @@ | System.Data;DataRelationCollection;false;Add;(System.Data.DataRelation);;Argument[0];Argument[Qualifier].Element;value;manual | | System.Data;DataRelationCollection;false;CopyTo;(System.Data.DataRelation[],System.Int32);;Argument[Qualifier].Element;Argument[0].Element;value;manual | | System.Data;DataRelationCollection;false;Remove;(System.Data.DataRelation);;Argument[0];Argument[Qualifier];taint;generated | -| System.Data;DataRelationCollection;true;Add;(System.Data.DataColumn,System.Data.DataColumn);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Data;DataRelationCollection;true;Add;(System.Data.DataColumn[],System.Data.DataColumn[]);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn,System.Data.DataColumn);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn,System.Data.DataColumn,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | | System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn,System.Data.DataColumn,System.Boolean);;Argument[0];ReturnValue;taint;generated | -| System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn,System.Data.DataColumn,System.Boolean);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn[],System.Data.DataColumn[]);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn[],System.Data.DataColumn[],System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | | System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn[],System.Data.DataColumn[],System.Boolean);;Argument[0];ReturnValue;taint;generated | -| System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn[],System.Data.DataColumn[],System.Boolean);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data;DataRelationCollection;true;AddRange;(System.Data.DataRelation[]);;Argument[0].Element;Argument[Qualifier].Element;value;manual | | System.Data;DataRow;false;DataRow;(System.Data.DataRowBuilder);;Argument[0];Argument[Qualifier];taint;generated | | System.Data;DataRow;false;GetChildRows;(System.Data.DataRelation);;Argument[Qualifier];ReturnValue;taint;generated | @@ -2267,12 +2207,10 @@ | System.Data;DataTable;false;set_PrimaryKey;(System.Data.DataColumn[]);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.Data;DataTable;false;set_Site;(System.ComponentModel.ISite);;Argument[0];Argument[Qualifier];taint;generated | | System.Data;DataTable;false;set_TableName;(System.String);;Argument[0];Argument[Qualifier];taint;generated | -| System.Data;DataTableCollection;false;Add;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Data;DataTableCollection;false;Add;(System.Data.DataTable);;Argument[0];Argument[Qualifier].Element;value;manual | | System.Data;DataTableCollection;false;Add;(System.String);;Argument[0];Argument[Qualifier].Element;value;manual | | System.Data;DataTableCollection;false;Add;(System.String,System.String);;Argument[1];Argument[Qualifier];taint;generated | | System.Data;DataTableCollection;false;Add;(System.String,System.String);;Argument[1];ReturnValue;taint;generated | -| System.Data;DataTableCollection;false;Add;(System.String,System.String);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data;DataTableCollection;false;AddRange;(System.Data.DataTable[]);;Argument[0].Element;Argument[Qualifier].Element;value;manual | | System.Data;DataTableCollection;false;CopyTo;(System.Data.DataTable[],System.Int32);;Argument[Qualifier].Element;Argument[0].Element;value;manual | | System.Data;DataTableCollection;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | @@ -2867,13 +2805,10 @@ | System.IO.Compression;BrotliStream;false;BeginWrite;(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object);;Argument[0].Element;Argument[Qualifier];taint;manual | | System.IO.Compression;BrotliStream;false;BrotliStream;(System.IO.Stream,System.IO.Compression.CompressionMode,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | | System.IO.Compression;BrotliStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | -| System.IO.Compression;BrotliStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO.Compression;BrotliStream;false;Read;(System.Byte[],System.Int32,System.Int32);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.IO.Compression;BrotliStream;false;ReadAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0].Element;taint;manual | -| System.IO.Compression;BrotliStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO.Compression;BrotliStream;false;Write;(System.Byte[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;manual | | System.IO.Compression;BrotliStream;false;WriteAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[0].Element;Argument[Qualifier];taint;manual | -| System.IO.Compression;BrotliStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO.Compression;BrotliStream;false;get_BaseStream;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO.Compression;DeflateStream;false;BeginRead;(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.IO.Compression;DeflateStream;false;BeginWrite;(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object);;Argument[0].Element;Argument[Qualifier];taint;manual | @@ -2884,28 +2819,22 @@ | System.IO.Compression;DeflateStream;false;DeflateStream;(System.IO.Stream,System.IO.Compression.CompressionMode);;Argument[0];ReturnValue;taint;manual | | System.IO.Compression;DeflateStream;false;DeflateStream;(System.IO.Stream,System.IO.Compression.CompressionMode,System.Boolean);;Argument[0];ReturnValue;taint;manual | | System.IO.Compression;DeflateStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | -| System.IO.Compression;DeflateStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO.Compression;DeflateStream;false;Read;(System.Byte[],System.Int32,System.Int32);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.IO.Compression;DeflateStream;false;ReadAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0].Element;taint;manual | -| System.IO.Compression;DeflateStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO.Compression;DeflateStream;false;Write;(System.Byte[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;manual | | System.IO.Compression;DeflateStream;false;WriteAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[0].Element;Argument[Qualifier];taint;manual | -| System.IO.Compression;DeflateStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO.Compression;DeflateStream;false;get_BaseStream;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO.Compression;GZipStream;false;BeginRead;(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.IO.Compression;GZipStream;false;BeginWrite;(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object);;Argument[0].Element;Argument[Qualifier];taint;manual | | System.IO.Compression;GZipStream;false;CopyTo;(System.IO.Stream,System.Int32);;Argument[Qualifier];Argument[0];taint;manual | | System.IO.Compression;GZipStream;false;CopyToAsync;(System.IO.Stream,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;manual | | System.IO.Compression;GZipStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | -| System.IO.Compression;GZipStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO.Compression;GZipStream;false;GZipStream;(System.IO.Stream,System.IO.Compression.CompressionLevel,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | | System.IO.Compression;GZipStream;false;GZipStream;(System.IO.Stream,System.IO.Compression.CompressionMode,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | | System.IO.Compression;GZipStream;false;Read;(System.Byte[],System.Int32,System.Int32);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.IO.Compression;GZipStream;false;ReadAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0].Element;taint;manual | -| System.IO.Compression;GZipStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO.Compression;GZipStream;false;Write;(System.Byte[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;manual | | System.IO.Compression;GZipStream;false;WriteAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[0].Element;Argument[Qualifier];taint;manual | -| System.IO.Compression;GZipStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO.Compression;GZipStream;false;get_BaseStream;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO.Compression;ZipArchive;false;CreateEntry;(System.String);;Argument[0];ReturnValue;taint;generated | | System.IO.Compression;ZipArchive;false;CreateEntry;(System.String);;Argument[Qualifier];ReturnValue;taint;generated | @@ -2939,7 +2868,6 @@ | System.IO.IsolatedStorage;IsolatedStorageFileStream;false;BeginRead;(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.IO.IsolatedStorage;IsolatedStorageFileStream;false;BeginWrite;(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object);;Argument[0].Element;Argument[Qualifier];taint;manual | | System.IO.IsolatedStorage;IsolatedStorageFileStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | -| System.IO.IsolatedStorage;IsolatedStorageFileStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO.IsolatedStorage;IsolatedStorageFileStream;false;Read;(System.Byte[],System.Int32,System.Int32);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.IO.IsolatedStorage;IsolatedStorageFileStream;false;ReadAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.IO.IsolatedStorage;IsolatedStorageFileStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | @@ -2965,14 +2893,11 @@ | System.IO.Pipes;NamedPipeServerStream;false;WaitForConnectionAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.IO.Pipes;PipeStream;false;BeginRead;(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.IO.Pipes;PipeStream;false;BeginWrite;(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object);;Argument[0].Element;Argument[Qualifier];taint;manual | -| System.IO.Pipes;PipeStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO.Pipes;PipeStream;false;InitializeHandle;(Microsoft.Win32.SafeHandles.SafePipeHandle,System.Boolean,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | | System.IO.Pipes;PipeStream;false;Read;(System.Byte[],System.Int32,System.Int32);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.IO.Pipes;PipeStream;false;ReadAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0].Element;taint;manual | -| System.IO.Pipes;PipeStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO.Pipes;PipeStream;false;Write;(System.Byte[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;manual | | System.IO.Pipes;PipeStream;false;WriteAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[0].Element;Argument[Qualifier];taint;manual | -| System.IO.Pipes;PipeStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO.Pipes;PipeStream;false;get_SafePipeHandle;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;BinaryReader;false;BinaryReader;(System.IO.Stream,System.Text.Encoding,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;BinaryReader;false;BinaryReader;(System.IO.Stream,System.Text.Encoding,System.Boolean);;Argument[1];Argument[Qualifier];taint;generated | @@ -2982,7 +2907,6 @@ | System.IO;BinaryReader;false;get_BaseStream;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;BinaryWriter;false;BinaryWriter;(System.IO.Stream,System.Text.Encoding,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;BinaryWriter;false;BinaryWriter;(System.IO.Stream,System.Text.Encoding,System.Boolean);;Argument[1];Argument[Qualifier];taint;generated | -| System.IO;BinaryWriter;false;DisposeAsync;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;BinaryWriter;false;Write;(System.Byte[]);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.IO;BinaryWriter;false;Write;(System.Byte[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.IO;BinaryWriter;false;get_BaseStream;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -2991,13 +2915,10 @@ | System.IO;BufferedStream;false;BufferedStream;(System.IO.Stream,System.Int32);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;BufferedStream;false;CopyTo;(System.IO.Stream,System.Int32);;Argument[Qualifier];Argument[0];taint;manual | | System.IO;BufferedStream;false;CopyToAsync;(System.IO.Stream,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;manual | -| System.IO;BufferedStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;BufferedStream;false;Read;(System.Byte[],System.Int32,System.Int32);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.IO;BufferedStream;false;ReadAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0].Element;taint;manual | -| System.IO;BufferedStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;BufferedStream;false;Write;(System.Byte[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;manual | | System.IO;BufferedStream;false;WriteAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[0].Element;Argument[Qualifier];taint;manual | -| System.IO;BufferedStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;BufferedStream;false;get_UnderlyingStream;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;Directory;false;CreateDirectory;(System.String);;Argument[0];ReturnValue;taint;generated | | System.IO;Directory;false;GetParent;(System.String);;Argument[0];ReturnValue;taint;generated | @@ -3057,7 +2978,6 @@ | System.IO;FileStream;false;BeginWrite;(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object);;Argument[0].Element;Argument[Qualifier];taint;manual | | System.IO;FileStream;false;CopyToAsync;(System.IO.Stream,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;manual | | System.IO;FileStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | -| System.IO;FileStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;FileStream;false;Read;(System.Byte[],System.Int32,System.Int32);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.IO;FileStream;false;ReadAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.IO;FileStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | @@ -3091,7 +3011,6 @@ | System.IO;MemoryStream;false;CopyTo;(System.IO.Stream,System.Int32);;Argument[Qualifier];Argument[0];taint;manual | | System.IO;MemoryStream;false;CopyToAsync;(System.IO.Stream,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;manual | | System.IO;MemoryStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | -| System.IO;MemoryStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;MemoryStream;false;GetBuffer;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;MemoryStream;false;MemoryStream;(System.Byte[]);;Argument[0];ReturnValue;taint;manual | | System.IO;MemoryStream;false;MemoryStream;(System.Byte[],System.Boolean);;Argument[0].Element;ReturnValue;taint;manual | @@ -3100,12 +3019,10 @@ | System.IO;MemoryStream;false;MemoryStream;(System.Byte[],System.Int32,System.Int32,System.Boolean,System.Boolean);;Argument[0].Element;ReturnValue;taint;manual | | System.IO;MemoryStream;false;Read;(System.Byte[],System.Int32,System.Int32);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.IO;MemoryStream;false;ReadAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0].Element;taint;manual | -| System.IO;MemoryStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;MemoryStream;false;ToArray;();;Argument[Qualifier];ReturnValue;taint;manual | | System.IO;MemoryStream;false;TryGetBuffer;(System.ArraySegment);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;MemoryStream;false;Write;(System.Byte[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;manual | | System.IO;MemoryStream;false;WriteAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[0].Element;Argument[Qualifier];taint;manual | -| System.IO;MemoryStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;MemoryStream;false;WriteTo;(System.IO.Stream);;Argument[Qualifier];Argument[0];taint;generated | | System.IO;Path;false;ChangeExtension;(System.String,System.String);;Argument[0];ReturnValue;taint;generated | | System.IO;Path;false;Combine;(System.String,System.String);;Argument[0];ReturnValue;taint;manual | @@ -3150,7 +3067,6 @@ | System.IO;Stream;false;CopyToAsync;(System.IO.Stream);;Argument[Qualifier];Argument[0];taint;manual | | System.IO;Stream;false;CopyToAsync;(System.IO.Stream,System.Int32);;Argument[Qualifier];Argument[0];taint;manual | | System.IO;Stream;false;CopyToAsync;(System.IO.Stream,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;manual | -| System.IO;Stream;false;FlushAsync;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;Stream;false;ReadAsync;(System.Byte[],System.Int32,System.Int32);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.IO;Stream;false;Synchronized;(System.IO.Stream);;Argument[0];ReturnValue;taint;generated | | System.IO;Stream;false;WriteAsync;(System.Byte[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;manual | @@ -3158,13 +3074,10 @@ | System.IO;Stream;true;BeginWrite;(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object);;Argument[0].Element;Argument[Qualifier];taint;manual | | System.IO;Stream;true;CopyTo;(System.IO.Stream,System.Int32);;Argument[Qualifier];Argument[0];taint;manual | | System.IO;Stream;true;CopyToAsync;(System.IO.Stream,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;manual | -| System.IO;Stream;true;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;Stream;true;Read;(System.Byte[],System.Int32,System.Int32);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.IO;Stream;true;ReadAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0].Element;taint;manual | -| System.IO;Stream;true;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;Stream;true;Write;(System.Byte[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;manual | | System.IO;Stream;true;WriteAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[0].Element;Argument[Qualifier];taint;manual | -| System.IO;Stream;true;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;StreamReader;false;Read;();;Argument[Qualifier];ReturnValue;taint;manual | | System.IO;StreamReader;false;Read;(System.Char[],System.Int32,System.Int32);;Argument[Qualifier];ReturnValue;taint;manual | | System.IO;StreamReader;false;Read;(System.Span);;Argument[Qualifier];ReturnValue;taint;manual | @@ -3182,7 +3095,6 @@ | System.IO;StreamReader;false;StreamReader;(System.IO.Stream,System.Text.Encoding,System.Boolean,System.Int32,System.Boolean);;Argument[1];Argument[Qualifier];taint;generated | | System.IO;StreamReader;false;get_BaseStream;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;StreamReader;false;get_CurrentEncoding;();;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;StreamWriter;false;FlushAsync;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;StreamWriter;false;StreamWriter;(System.IO.Stream,System.Text.Encoding,System.Int32,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;StreamWriter;false;StreamWriter;(System.IO.Stream,System.Text.Encoding,System.Int32,System.Boolean);;Argument[1];Argument[Qualifier];taint;generated | | System.IO;StreamWriter;false;Write;(System.Char[]);;Argument[0].Element;Argument[Qualifier];taint;generated | @@ -3197,14 +3109,7 @@ | System.IO;StreamWriter;false;Write;(System.String,System.Object,System.Object,System.Object);;Argument[3];Argument[Qualifier];taint;generated | | System.IO;StreamWriter;false;Write;(System.String,System.Object[]);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;StreamWriter;false;Write;(System.String,System.Object[]);;Argument[1].Element;Argument[Qualifier];taint;generated | -| System.IO;StreamWriter;false;WriteAsync;(System.Char);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;StreamWriter;false;WriteAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;ReturnValue;taint;generated | -| System.IO;StreamWriter;false;WriteAsync;(System.Char[],System.Int32,System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;StreamWriter;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.IO;StreamWriter;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | -| System.IO;StreamWriter;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;StreamWriter;false;WriteAsync;(System.String);;Argument[0];ReturnValue;taint;generated | -| System.IO;StreamWriter;false;WriteAsync;(System.String);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;StreamWriter;false;WriteLine;(System.String);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;StreamWriter;false;WriteLine;(System.String,System.Object);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;StreamWriter;false;WriteLine;(System.String,System.Object);;Argument[1];Argument[Qualifier];taint;generated | @@ -3217,15 +3122,7 @@ | System.IO;StreamWriter;false;WriteLine;(System.String,System.Object,System.Object,System.Object);;Argument[3];Argument[Qualifier];taint;generated | | System.IO;StreamWriter;false;WriteLine;(System.String,System.Object[]);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;StreamWriter;false;WriteLine;(System.String,System.Object[]);;Argument[1].Element;Argument[Qualifier];taint;generated | -| System.IO;StreamWriter;false;WriteLineAsync;();;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;StreamWriter;false;WriteLineAsync;(System.Char);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;StreamWriter;false;WriteLineAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;ReturnValue;taint;generated | -| System.IO;StreamWriter;false;WriteLineAsync;(System.Char[],System.Int32,System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;StreamWriter;false;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.IO;StreamWriter;false;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | -| System.IO;StreamWriter;false;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;StreamWriter;false;WriteLineAsync;(System.String);;Argument[0];ReturnValue;taint;generated | -| System.IO;StreamWriter;false;WriteLineAsync;(System.String);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;StreamWriter;false;get_BaseStream;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;StreamWriter;false;get_Encoding;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;StringReader;false;Read;();;Argument[Qualifier];ReturnValue;taint;manual | @@ -3241,39 +3138,19 @@ | System.IO;StringReader;false;ReadToEnd;();;Argument[Qualifier];ReturnValue;taint;manual | | System.IO;StringReader;false;ReadToEndAsync;();;Argument[Qualifier];ReturnValue;taint;manual | | System.IO;StringReader;false;StringReader;(System.String);;Argument[0];ReturnValue;taint;manual | -| System.IO;StringWriter;false;FlushAsync;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;StringWriter;false;GetStringBuilder;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;StringWriter;false;StringWriter;(System.Text.StringBuilder,System.IFormatProvider);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;StringWriter;false;ToString;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;StringWriter;false;Write;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.IO;StringWriter;false;Write;(System.String);;Argument[0];Argument[Qualifier];taint;generated | -| System.IO;StringWriter;false;Write;(System.Text.StringBuilder);;Argument[0];Argument[Qualifier];taint;generated | -| System.IO;StringWriter;false;WriteAsync;(System.Char);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;StringWriter;false;WriteAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated | -| System.IO;StringWriter;false;WriteAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;ReturnValue;taint;generated | -| System.IO;StringWriter;false;WriteAsync;(System.Char[],System.Int32,System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;StringWriter;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.IO;StringWriter;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | -| System.IO;StringWriter;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;StringWriter;false;WriteAsync;(System.String);;Argument[0];Argument[Qualifier];taint;generated | -| System.IO;StringWriter;false;WriteAsync;(System.String);;Argument[0];ReturnValue;taint;generated | -| System.IO;StringWriter;false;WriteAsync;(System.String);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;StringWriter;false;WriteAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;StringWriter;false;WriteAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | -| System.IO;StringWriter;false;WriteLine;(System.Text.StringBuilder);;Argument[0];Argument[Qualifier];taint;generated | -| System.IO;StringWriter;false;WriteLineAsync;(System.Char);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;StringWriter;false;WriteLineAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated | -| System.IO;StringWriter;false;WriteLineAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;ReturnValue;taint;generated | -| System.IO;StringWriter;false;WriteLineAsync;(System.Char[],System.Int32,System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;StringWriter;false;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.IO;StringWriter;false;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | -| System.IO;StringWriter;false;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;StringWriter;false;WriteLineAsync;(System.String);;Argument[0];Argument[Qualifier];taint;generated | -| System.IO;StringWriter;false;WriteLineAsync;(System.String);;Argument[0];ReturnValue;taint;generated | -| System.IO;StringWriter;false;WriteLineAsync;(System.String);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;StringWriter;false;WriteLineAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;StringWriter;false;WriteLineAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | -| System.IO;StringWriter;false;WriteLineAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;TextReader;false;Synchronized;(System.IO.TextReader);;Argument[0];ReturnValue;taint;generated | | System.IO;TextReader;true;Read;();;Argument[Qualifier];ReturnValue;taint;manual | | System.IO;TextReader;true;Read;(System.Char[],System.Int32,System.Int32);;Argument[Qualifier];ReturnValue;taint;manual | @@ -3291,12 +3168,7 @@ | System.IO;TextWriter;false;Synchronized;(System.IO.TextWriter);;Argument[0];ReturnValue;taint;generated | | System.IO;TextWriter;false;TextWriter;(System.IFormatProvider);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;TextWriter;false;WriteAsync;(System.Char[]);;Argument[0].Element;Argument[Qualifier];taint;generated | -| System.IO;TextWriter;false;WriteAsync;(System.Char[]);;Argument[0].Element;ReturnValue;taint;generated | -| System.IO;TextWriter;false;WriteAsync;(System.Char[]);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;TextWriter;false;WriteLineAsync;(System.Char[]);;Argument[0].Element;Argument[Qualifier];taint;generated | -| System.IO;TextWriter;false;WriteLineAsync;(System.Char[]);;Argument[0].Element;ReturnValue;taint;generated | -| System.IO;TextWriter;false;WriteLineAsync;(System.Char[]);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;TextWriter;true;FlushAsync;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;TextWriter;true;Write;(System.Char[]);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.IO;TextWriter;true;Write;(System.Object);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;TextWriter;true;Write;(System.String,System.Object);;Argument[0];Argument[Qualifier];taint;generated | @@ -3310,14 +3182,7 @@ | System.IO;TextWriter;true;Write;(System.String,System.Object,System.Object,System.Object);;Argument[3];Argument[Qualifier];taint;generated | | System.IO;TextWriter;true;Write;(System.String,System.Object[]);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;TextWriter;true;Write;(System.String,System.Object[]);;Argument[1].Element;Argument[Qualifier];taint;generated | -| System.IO;TextWriter;true;WriteAsync;(System.Char);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteAsync;(System.Char[],System.Int32,System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.IO;TextWriter;true;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteAsync;(System.String);;Argument[0];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteAsync;(System.String);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;TextWriter;true;WriteAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | | System.IO;TextWriter;true;WriteLine;(System.Char[]);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.IO;TextWriter;true;WriteLine;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated | @@ -3334,18 +3199,8 @@ | System.IO;TextWriter;true;WriteLine;(System.String,System.Object,System.Object,System.Object);;Argument[3];Argument[Qualifier];taint;generated | | System.IO;TextWriter;true;WriteLine;(System.String,System.Object[]);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;TextWriter;true;WriteLine;(System.String,System.Object[]);;Argument[1].Element;Argument[Qualifier];taint;generated | -| System.IO;TextWriter;true;WriteLine;(System.Text.StringBuilder);;Argument[0];Argument[Qualifier];taint;generated | -| System.IO;TextWriter;true;WriteLineAsync;();;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteLineAsync;(System.Char);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteLineAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteLineAsync;(System.Char[],System.Int32,System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.IO;TextWriter;true;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteLineAsync;(System.String);;Argument[0];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteLineAsync;(System.String);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;TextWriter;true;WriteLineAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteLineAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;TextWriter;true;get_FormatProvider;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;TextWriter;true;get_NewLine;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;TextWriter;true;set_NewLine;(System.String);;Argument[0];Argument[Qualifier];taint;generated | @@ -3353,19 +3208,16 @@ | System.IO;UnmanagedMemoryAccessor;false;UnmanagedMemoryAccessor;(System.Runtime.InteropServices.SafeBuffer,System.Int64,System.Int64);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;UnmanagedMemoryAccessor;false;UnmanagedMemoryAccessor;(System.Runtime.InteropServices.SafeBuffer,System.Int64,System.Int64,System.IO.FileAccess);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;UnmanagedMemoryStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | -| System.IO;UnmanagedMemoryStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;UnmanagedMemoryStream;false;Initialize;(System.Byte*,System.Int64,System.Int64,System.IO.FileAccess);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;UnmanagedMemoryStream;false;Initialize;(System.Runtime.InteropServices.SafeBuffer,System.Int64,System.Int64,System.IO.FileAccess);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;UnmanagedMemoryStream;false;Read;(System.Byte[],System.Int32,System.Int32);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.IO;UnmanagedMemoryStream;false;ReadAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0].Element;taint;manual | -| System.IO;UnmanagedMemoryStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;UnmanagedMemoryStream;false;UnmanagedMemoryStream;(System.Byte*,System.Int64);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;UnmanagedMemoryStream;false;UnmanagedMemoryStream;(System.Byte*,System.Int64,System.Int64,System.IO.FileAccess);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;UnmanagedMemoryStream;false;UnmanagedMemoryStream;(System.Runtime.InteropServices.SafeBuffer,System.Int64,System.Int64);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;UnmanagedMemoryStream;false;UnmanagedMemoryStream;(System.Runtime.InteropServices.SafeBuffer,System.Int64,System.Int64,System.IO.FileAccess);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;UnmanagedMemoryStream;false;Write;(System.Byte[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;manual | | System.IO;UnmanagedMemoryStream;false;WriteAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[0].Element;Argument[Qualifier];taint;manual | -| System.IO;UnmanagedMemoryStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;UnmanagedMemoryStream;false;get_PositionPointer;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Linq.Expressions;BinaryExpression;false;Accept;(System.Linq.Expressions.ExpressionVisitor);;Argument[Qualifier];Argument[0];taint;generated | | System.Linq.Expressions;BinaryExpression;false;Accept;(System.Linq.Expressions.ExpressionVisitor);;Argument[Qualifier];ReturnValue;taint;generated | @@ -4687,29 +4539,18 @@ | System.Net.Http.Headers;WarningHeaderValue;false;get_Text;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http.Json;JsonContent;false;Create;(System.Object,System.Type,System.Net.Http.Headers.MediaTypeHeaderValue,System.Text.Json.JsonSerializerOptions);;Argument[3];ReturnValue;taint;generated | | System.Net.Http.Json;JsonContent;false;Create<>;(T,System.Net.Http.Headers.MediaTypeHeaderValue,System.Text.Json.JsonSerializerOptions);;Argument[2];ReturnValue;taint;generated | -| System.Net.Http.Json;JsonContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Net.Http.Json;JsonContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Http.Json;JsonContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;ByteArrayContent;false;ByteArrayContent;(System.Byte[]);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.Net.Http;ByteArrayContent;false;ByteArrayContent;(System.Byte[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.Net.Http;ByteArrayContent;false;CreateContentReadStream;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Net.Http;ByteArrayContent;false;CreateContentReadStreamAsync;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;ByteArrayContent;false;SerializeToStream;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[0];ReturnValue;taint;generated | | System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | -| System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[2];ReturnValue;taint;generated | | System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;DelegatingHandler;false;DelegatingHandler;(System.Net.Http.HttpMessageHandler);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Http;DelegatingHandler;false;SendAsync;(System.Net.Http.HttpRequestMessage,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | | System.Net.Http;DelegatingHandler;false;get_InnerHandler;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;DelegatingHandler;false;set_InnerHandler;(System.Net.Http.HttpMessageHandler);;Argument[0];Argument[Qualifier];taint;generated | -| System.Net.Http;FormUrlEncodedContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | -| System.Net.Http;FormUrlEncodedContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[2];ReturnValue;taint;generated | | System.Net.Http;FormUrlEncodedContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Http;FormUrlEncodedContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;HttpClient;false;Send;(System.Net.Http.HttpRequestMessage);;Argument[Qualifier];Argument[0];taint;generated | | System.Net.Http;HttpClient;false;Send;(System.Net.Http.HttpRequestMessage,System.Net.Http.HttpCompletionOption);;Argument[Qualifier];Argument[0];taint;generated | | System.Net.Http;HttpClient;false;Send;(System.Net.Http.HttpRequestMessage,System.Net.Http.HttpCompletionOption,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated | @@ -4736,10 +4577,7 @@ | System.Net.Http;HttpContent;false;ReadAsStreamAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;HttpContent;false;get_Headers;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;HttpContent;true;CreateContentReadStream;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Net.Http;HttpContent;true;CreateContentReadStreamAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Net.Http;HttpContent;true;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Net.Http;HttpContent;true;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Http;HttpContent;true;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;HttpMessageInvoker;false;HttpMessageInvoker;(System.Net.Http.HttpMessageHandler,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Http;HttpMessageInvoker;false;SendAsync;(System.Net.Http.HttpRequestMessage,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | | System.Net.Http;HttpMethod;false;HttpMethod;(System.String);;Argument[0];Argument[Qualifier];taint;generated | @@ -4781,27 +4619,19 @@ | System.Net.Http;MessageProcessingHandler;false;SendAsync;(System.Net.Http.HttpRequestMessage,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | | System.Net.Http;MultipartContent;false;Add;(System.Net.Http.HttpContent);;Argument[0];Argument[Qualifier].Element;value;manual | | System.Net.Http;MultipartContent;false;CreateContentReadStream;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Net.Http;MultipartContent;false;CreateContentReadStreamAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;MultipartContent;false;GetEnumerator;();;Argument[Qualifier].Element;ReturnValue.Property[System.Collections.Generic.IEnumerator<>.Current];value;manual | | System.Net.Http;MultipartContent;false;GetEnumerator;();;Argument[Qualifier].Element;ReturnValue.Property[System.Collections.IEnumerator.Current];value;manual | | System.Net.Http;MultipartContent;false;MultipartContent;(System.String,System.String);;Argument[1];Argument[Qualifier];taint;generated | | System.Net.Http;MultipartContent;false;SerializeToStream;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated | | System.Net.Http;MultipartContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Http;MultipartContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Net.Http;MultipartContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Http;MultipartContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;MultipartFormDataContent;false;Add;(System.Net.Http.HttpContent);;Argument[0];Argument[Qualifier].Element;value;manual | | System.Net.Http;MultipartFormDataContent;false;Add;(System.Net.Http.HttpContent,System.String);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Http;MultipartFormDataContent;false;Add;(System.Net.Http.HttpContent,System.String,System.String);;Argument[0];Argument[Qualifier];taint;generated | -| System.Net.Http;MultipartFormDataContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Net.Http;MultipartFormDataContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Http;MultipartFormDataContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;ReadOnlyMemoryContent;false;CreateContentReadStream;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Net.Http;ReadOnlyMemoryContent;false;CreateContentReadStreamAsync;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;ReadOnlyMemoryContent;false;ReadOnlyMemoryContent;(System.ReadOnlyMemory);;Argument[0];Argument[Qualifier];taint;generated | -| System.Net.Http;ReadOnlyMemoryContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Net.Http;ReadOnlyMemoryContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Http;ReadOnlyMemoryContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;SocketsHttpConnectionContext;false;get_DnsEndPoint;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;SocketsHttpConnectionContext;false;get_InitialRequestMessage;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;SocketsHttpHandler;false;get_ConnectCallback;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -4838,19 +4668,11 @@ | System.Net.Http;SocketsHttpPlaintextStreamFilterContext;false;get_PlaintextStream;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;StreamContent;false;CreateContentReadStream;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;StreamContent;false;SerializeToStream;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[0];ReturnValue;taint;generated | | System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | -| System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[2];ReturnValue;taint;generated | | System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;StreamContent;false;StreamContent;(System.IO.Stream);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Http;StreamContent;false;StreamContent;(System.IO.Stream,System.Int32);;Argument[0];Argument[Qualifier];taint;generated | -| System.Net.Http;StringContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | -| System.Net.Http;StringContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[2];ReturnValue;taint;generated | | System.Net.Http;StringContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Http;StringContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Mail;AlternateView;false;CreateAlternateViewFromString;(System.String);;Argument[0];ReturnValue;taint;generated | | System.Net.Mail;AlternateView;false;CreateAlternateViewFromString;(System.String,System.Net.Mime.ContentType);;Argument[0];ReturnValue;taint;generated | | System.Net.Mail;AlternateView;false;CreateAlternateViewFromString;(System.String,System.Net.Mime.ContentType);;Argument[1];ReturnValue;taint;generated | @@ -4996,7 +4818,6 @@ | System.Net.NetworkInformation;UnicastIPAddressInformationCollection;false;GetEnumerator;();;Argument[Qualifier].Element;ReturnValue.Property[System.Collections.IEnumerator.Current];value;manual | | System.Net.NetworkInformation;UnicastIPAddressInformationCollection;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Security;AuthenticatedStream;false;AuthenticatedStream;(System.IO.Stream,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | -| System.Net.Security;AuthenticatedStream;false;DisposeAsync;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Security;AuthenticatedStream;false;get_InnerStream;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Security;NegotiateStream;false;AuthenticateAsClient;(System.Net.NetworkCredential,System.Security.Authentication.ExtendedProtection.ChannelBinding,System.String);;Argument[1];Argument[Qualifier];taint;generated | | System.Net.Security;NegotiateStream;false;AuthenticateAsClient;(System.Net.NetworkCredential,System.Security.Authentication.ExtendedProtection.ChannelBinding,System.String);;Argument[2];Argument[Qualifier];taint;generated | @@ -5017,7 +4838,6 @@ | System.Net.Security;NegotiateStream;false;BeginRead;(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.Net.Security;NegotiateStream;false;BeginWrite;(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object);;Argument[0].Element;Argument[Qualifier];taint;manual | | System.Net.Security;NegotiateStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | -| System.Net.Security;NegotiateStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Security;NegotiateStream;false;Read;(System.Byte[],System.Int32,System.Int32);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.Net.Security;NegotiateStream;false;ReadAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.Net.Security;NegotiateStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | @@ -5034,14 +4854,11 @@ | System.Net.Security;SslStream;false;BeginRead;(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.Net.Security;SslStream;false;BeginWrite;(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object);;Argument[0].Element;Argument[Qualifier];taint;manual | | System.Net.Security;SslStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | -| System.Net.Security;SslStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Security;SslStream;false;Read;(System.Byte[],System.Int32,System.Int32);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.Net.Security;SslStream;false;ReadAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0].Element;taint;manual | -| System.Net.Security;SslStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Security;SslStream;false;Write;(System.Byte[]);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.Net.Security;SslStream;false;Write;(System.Byte[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;manual | | System.Net.Security;SslStream;false;WriteAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[0].Element;Argument[Qualifier];taint;manual | -| System.Net.Security;SslStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Security;SslStream;false;get_LocalCertificate;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Security;SslStream;false;get_NegotiatedApplicationProtocol;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Security;SslStream;false;get_RemoteCertificate;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -5062,7 +4879,6 @@ | System.Net.Sockets;MulticastOption;false;set_LocalAddress;(System.Net.IPAddress);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Sockets;NetworkStream;false;BeginRead;(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.Net.Sockets;NetworkStream;false;BeginWrite;(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object);;Argument[0].Element;Argument[Qualifier];taint;manual | -| System.Net.Sockets;NetworkStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Sockets;NetworkStream;false;NetworkStream;(System.Net.Sockets.Socket,System.IO.FileAccess,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Sockets;NetworkStream;false;Read;(System.Byte[],System.Int32,System.Int32);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.Net.Sockets;NetworkStream;false;ReadAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0].Element;taint;manual | @@ -5084,7 +4900,6 @@ | System.Net.Sockets;Socket;false;ConnectAsync;(System.Net.Sockets.SocketAsyncEventArgs);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Sockets;Socket;false;ConnectAsync;(System.Net.Sockets.SocketAsyncEventArgs);;Argument[Qualifier];Argument[0];taint;generated | | System.Net.Sockets;Socket;false;DisconnectAsync;(System.Net.Sockets.SocketAsyncEventArgs);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Sockets;Socket;false;EndAccept;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | | System.Net.Sockets;Socket;false;ReceiveAsync;(System.Net.Sockets.SocketAsyncEventArgs);;Argument[Qualifier];Argument[0];taint;generated | | System.Net.Sockets;Socket;false;ReceiveFrom;(System.Byte[],System.Int32,System.Int32,System.Net.Sockets.SocketFlags,System.Net.EndPoint);;Argument[4];Argument[Qualifier];taint;generated | | System.Net.Sockets;Socket;false;ReceiveFrom;(System.Byte[],System.Int32,System.Int32,System.Net.Sockets.SocketFlags,System.Net.EndPoint);;Argument[4];ReturnValue;taint;generated | @@ -5127,7 +4942,6 @@ | System.Net.Sockets;SocketAsyncEventArgs;false;set_SendPacketsElements;(System.Net.Sockets.SendPacketsElement[]);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.Net.Sockets;SocketAsyncEventArgs;false;set_UserToken;(System.Object);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Sockets;SocketException;false;get_Message;();;Argument[Qualifier];ReturnValue;taint;generated | -| System.Net.Sockets;SocketTaskExtensions;false;AcceptAsync;(System.Net.Sockets.Socket,System.Net.Sockets.Socket);;Argument[1];ReturnValue;taint;generated | | System.Net.Sockets;SocketTaskExtensions;false;ConnectAsync;(System.Net.Sockets.Socket,System.Net.EndPoint);;Argument[1];Argument[0];taint;generated | | System.Net.Sockets;SocketTaskExtensions;false;ConnectAsync;(System.Net.Sockets.Socket,System.Net.EndPoint,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Net.Sockets;SocketTaskExtensions;false;ConnectAsync;(System.Net.Sockets.Socket,System.Net.EndPoint,System.Threading.CancellationToken);;Argument[1];Argument[0];taint;generated | @@ -5139,8 +4953,6 @@ | System.Net.Sockets;SocketTaskExtensions;false;ReceiveAsync;(System.Net.Sockets.Socket,System.Memory,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Net.Sockets;SocketTaskExtensions;false;ReceiveAsync;(System.Net.Sockets.Socket,System.Memory,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | | System.Net.Sockets;SocketTaskExtensions;false;ReceiveAsync;(System.Net.Sockets.Socket,System.Memory,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken);;Argument[3];ReturnValue;taint;generated | -| System.Net.Sockets;SocketTaskExtensions;false;ReceiveFromAsync;(System.Net.Sockets.Socket,System.ArraySegment,System.Net.Sockets.SocketFlags,System.Net.EndPoint);;Argument[3];ReturnValue;taint;generated | -| System.Net.Sockets;SocketTaskExtensions;false;ReceiveMessageFromAsync;(System.Net.Sockets.Socket,System.ArraySegment,System.Net.Sockets.SocketFlags,System.Net.EndPoint);;Argument[3];ReturnValue;taint;generated | | System.Net.Sockets;SocketTaskExtensions;false;SendAsync;(System.Net.Sockets.Socket,System.ReadOnlyMemory,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Net.Sockets;SocketTaskExtensions;false;SendAsync;(System.Net.Sockets.Socket,System.ReadOnlyMemory,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken);;Argument[3];ReturnValue;taint;generated | | System.Net.Sockets;SocketTaskExtensions;false;SendToAsync;(System.Net.Sockets.Socket,System.ArraySegment,System.Net.Sockets.SocketFlags,System.Net.EndPoint);;Argument[3];Argument[0];taint;generated | @@ -5151,8 +4963,6 @@ | System.Net.Sockets;TcpClient;false;set_Client;(System.Net.Sockets.Socket);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Sockets;TcpListener;false;AcceptSocket;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Sockets;TcpListener;false;AcceptTcpClient;();;Argument[Qualifier];ReturnValue;taint;generated | -| System.Net.Sockets;TcpListener;false;EndAcceptSocket;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | -| System.Net.Sockets;TcpListener;false;EndAcceptTcpClient;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | | System.Net.Sockets;TcpListener;false;TcpListener;(System.Net.IPAddress,System.Int32);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Sockets;TcpListener;false;TcpListener;(System.Net.IPEndPoint);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Sockets;TcpListener;false;get_LocalEndpoint;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -5230,17 +5040,11 @@ | System.Net;CookieException;false;GetObjectData;(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext);;Argument[Qualifier];Argument[0];taint;generated | | System.Net;CredentialCache;false;GetCredential;(System.Uri,System.String);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net;CredentialCache;false;GetEnumerator;();;Argument[Qualifier].Element;ReturnValue.Property[System.Collections.IEnumerator.Current];value;manual | -| System.Net;Dns;false;EndGetHostAddresses;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | -| System.Net;Dns;false;EndGetHostByName;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | -| System.Net;Dns;false;EndGetHostEntry;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | -| System.Net;Dns;false;EndResolve;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | | System.Net;DnsEndPoint;false;DnsEndPoint;(System.String,System.Int32,System.Net.Sockets.AddressFamily);;Argument[0];Argument[Qualifier];taint;generated | | System.Net;DnsEndPoint;false;ToString;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net;DnsEndPoint;false;get_Host;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net;DownloadDataCompletedEventArgs;false;get_Result;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net;DownloadStringCompletedEventArgs;false;get_Result;();;Argument[Qualifier];ReturnValue;taint;generated | -| System.Net;FileWebRequest;false;EndGetRequestStream;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | -| System.Net;FileWebRequest;false;EndGetResponse;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | | System.Net;FileWebRequest;false;GetRequestStream;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net;FileWebRequest;false;GetResponse;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net;FileWebRequest;false;get_ContentType;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -5319,9 +5123,6 @@ | System.Net;HttpListenerTimeoutManager;false;get_IdleConnection;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net;HttpListenerTimeoutManager;false;set_DrainEntityBody;(System.TimeSpan);;Argument[0];Argument[Qualifier];taint;generated | | System.Net;HttpListenerTimeoutManager;false;set_IdleConnection;(System.TimeSpan);;Argument[0];Argument[Qualifier];taint;generated | -| System.Net;HttpWebRequest;false;EndGetRequestStream;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | -| System.Net;HttpWebRequest;false;EndGetRequestStream;(System.IAsyncResult,System.Net.TransportContext);;Argument[0];ReturnValue;taint;generated | -| System.Net;HttpWebRequest;false;EndGetResponse;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | | System.Net;HttpWebRequest;false;GetRequestStream;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net;HttpWebRequest;false;GetRequestStream;(System.Net.TransportContext);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net;HttpWebRequest;false;GetResponse;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -5407,8 +5208,6 @@ | System.Net;WebClient;false;GetWebResponse;(System.Net.WebRequest);;Argument[0];ReturnValue;taint;generated | | System.Net;WebClient;false;GetWebResponse;(System.Net.WebRequest,System.IAsyncResult);;Argument[0];Argument[Qualifier];taint;generated | | System.Net;WebClient;false;GetWebResponse;(System.Net.WebRequest,System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | -| System.Net;WebClient;false;GetWebResponse;(System.Net.WebRequest,System.IAsyncResult);;Argument[1];Argument[Qualifier];taint;generated | -| System.Net;WebClient;false;GetWebResponse;(System.Net.WebRequest,System.IAsyncResult);;Argument[1];ReturnValue;taint;generated | | System.Net;WebClient;false;OpenRead;(System.String);;Argument[0];Argument[Qualifier];taint;generated | | System.Net;WebClient;false;OpenRead;(System.String);;Argument[0];ReturnValue;taint;generated | | System.Net;WebClient;false;OpenRead;(System.String);;Argument[Qualifier];ReturnValue;taint;generated | @@ -6342,7 +6141,6 @@ | System.Resources;ResourceReader;false;GetResourceData;(System.String,System.String,System.Byte[]);;Argument[Qualifier];ReturnValue;taint;generated | | System.Resources;ResourceReader;false;ResourceReader;(System.IO.Stream);;Argument[0];Argument[Qualifier];taint;generated | | System.Resources;ResourceSet;false;GetEnumerator;();;Argument[Qualifier].Element;ReturnValue.Property[System.Collections.IEnumerator.Current];value;manual | -| System.Resources;ResourceSet;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Resources;ResourceSet;false;ResourceSet;(System.IO.Stream);;Argument[0];Argument[Qualifier];taint;generated | | System.Resources;ResourceSet;false;ResourceSet;(System.Resources.IResourceReader);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.Resources;ResourceWriter;false;ResourceWriter;(System.IO.Stream);;Argument[0];Argument[Qualifier];taint;generated | @@ -6725,7 +6523,6 @@ | System.Security.Cryptography.X509Certificates;X509Certificate;false;ToString;(System.Boolean);;Argument[Qualifier];ReturnValue;taint;generated | | System.Security.Cryptography.X509Certificates;X509Certificate;false;get_Issuer;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Security.Cryptography.X509Certificates;X509Certificate;false;get_Subject;();;Argument[Qualifier];ReturnValue;taint;generated | -| System.Security.Cryptography.X509Certificates;X509CertificateCollection+X509CertificateEnumerator;false;X509CertificateEnumerator;(System.Security.Cryptography.X509Certificates.X509CertificateCollection);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.Security.Cryptography.X509Certificates;X509CertificateCollection+X509CertificateEnumerator;false;get_Current;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Security.Cryptography.X509Certificates;X509CertificateCollection;false;Add;(System.Security.Cryptography.X509Certificates.X509Certificate);;Argument[0];Argument[Qualifier].Element;value;manual | | System.Security.Cryptography.X509Certificates;X509CertificateCollection;false;AddRange;(System.Security.Cryptography.X509Certificates.X509CertificateCollection);;Argument[0].Element;Argument[Qualifier].Element;value;manual | @@ -6794,7 +6591,6 @@ | System.Security.Cryptography;CryptoStream;false;CryptoStream;(System.IO.Stream,System.Security.Cryptography.ICryptoTransform,System.Security.Cryptography.CryptoStreamMode,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | | System.Security.Cryptography;CryptoStream;false;CryptoStream;(System.IO.Stream,System.Security.Cryptography.ICryptoTransform,System.Security.Cryptography.CryptoStreamMode,System.Boolean);;Argument[1];Argument[Qualifier];taint;generated | | System.Security.Cryptography;CryptoStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | -| System.Security.Cryptography;CryptoStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Security.Cryptography;CryptoStream;false;Read;(System.Byte[],System.Int32,System.Int32);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.Security.Cryptography;CryptoStream;false;ReadAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.Security.Cryptography;CryptoStream;false;Write;(System.Byte[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;manual | @@ -7313,10 +7109,6 @@ | System.Threading.Tasks.Dataflow;DataflowBlock;false;Receive<>;(System.Threading.Tasks.Dataflow.ISourceBlock,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Threading.Tasks.Dataflow;DataflowBlock;false;Receive<>;(System.Threading.Tasks.Dataflow.ISourceBlock,System.TimeSpan);;Argument[0];ReturnValue;taint;generated | | System.Threading.Tasks.Dataflow;DataflowBlock;false;Receive<>;(System.Threading.Tasks.Dataflow.ISourceBlock,System.TimeSpan,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | -| System.Threading.Tasks.Dataflow;DataflowBlock;false;ReceiveAsync<>;(System.Threading.Tasks.Dataflow.ISourceBlock);;Argument[0];ReturnValue;taint;generated | -| System.Threading.Tasks.Dataflow;DataflowBlock;false;ReceiveAsync<>;(System.Threading.Tasks.Dataflow.ISourceBlock,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | -| System.Threading.Tasks.Dataflow;DataflowBlock;false;ReceiveAsync<>;(System.Threading.Tasks.Dataflow.ISourceBlock,System.TimeSpan);;Argument[0];ReturnValue;taint;generated | -| System.Threading.Tasks.Dataflow;DataflowBlock;false;ReceiveAsync<>;(System.Threading.Tasks.Dataflow.ISourceBlock,System.TimeSpan,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Threading.Tasks.Dataflow;DataflowBlock;false;SendAsync<>;(System.Threading.Tasks.Dataflow.ITargetBlock,TInput,System.Threading.CancellationToken);;Argument[1];Argument[0];taint;generated | | System.Threading.Tasks.Dataflow;DataflowBlock;false;TryReceive<>;(System.Threading.Tasks.Dataflow.IReceivableSourceBlock,TOutput);;Argument[0];ReturnValue;taint;generated | | System.Threading.Tasks.Dataflow;DataflowBlockOptions;false;get_CancellationToken;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -8103,12 +7895,9 @@ | System.Xml.Schema;XmlSchemaSet;false;Add;(System.String,System.Xml.XmlReader);;Argument[Qualifier];ReturnValue;taint;generated | | System.Xml.Schema;XmlSchemaSet;false;Add;(System.Xml.Schema.XmlSchema);;Argument[0];Argument[Qualifier];taint;generated | | System.Xml.Schema;XmlSchemaSet;false;Add;(System.Xml.Schema.XmlSchema);;Argument[0];ReturnValue;taint;generated | -| System.Xml.Schema;XmlSchemaSet;false;Add;(System.Xml.Schema.XmlSchemaSet);;Argument[0];Argument[Qualifier];taint;generated | -| System.Xml.Schema;XmlSchemaSet;false;CopyTo;(System.Xml.Schema.XmlSchema[],System.Int32);;Argument[Qualifier];Argument[0].Element;taint;generated | | System.Xml.Schema;XmlSchemaSet;false;Remove;(System.Xml.Schema.XmlSchema);;Argument[0];ReturnValue;taint;generated | | System.Xml.Schema;XmlSchemaSet;false;Reprocess;(System.Xml.Schema.XmlSchema);;Argument[0];Argument[Qualifier];taint;generated | | System.Xml.Schema;XmlSchemaSet;false;Reprocess;(System.Xml.Schema.XmlSchema);;Argument[0];ReturnValue;taint;generated | -| System.Xml.Schema;XmlSchemaSet;false;Schemas;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Xml.Schema;XmlSchemaSet;false;XmlSchemaSet;(System.Xml.XmlNameTable);;Argument[0];Argument[Qualifier];taint;generated | | System.Xml.Schema;XmlSchemaSet;false;get_CompilationSettings;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Xml.Schema;XmlSchemaSet;false;get_GlobalAttributes;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -8195,10 +7984,8 @@ | System.Xml.Schema;XmlSchemaXPath;false;get_XPath;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Xml.Schema;XmlSchemaXPath;false;set_XPath;(System.String);;Argument[0];Argument[Qualifier];taint;generated | | System.Xml.Serialization;CodeIdentifiers;false;Add;(System.String,System.Object);;Argument[1];Argument[Qualifier];taint;generated | -| System.Xml.Serialization;CodeIdentifiers;false;Add;(System.String,System.Object);;Argument[Qualifier];Argument[1];taint;generated | | System.Xml.Serialization;CodeIdentifiers;false;AddUnique;(System.String,System.Object);;Argument[0];ReturnValue;taint;generated | | System.Xml.Serialization;CodeIdentifiers;false;AddUnique;(System.String,System.Object);;Argument[1];Argument[Qualifier];taint;generated | -| System.Xml.Serialization;CodeIdentifiers;false;AddUnique;(System.String,System.Object);;Argument[Qualifier];Argument[1];taint;generated | | System.Xml.Serialization;CodeIdentifiers;false;MakeUnique;(System.String);;Argument[0];ReturnValue;taint;generated | | System.Xml.Serialization;CodeIdentifiers;false;ToArray;(System.Type);;Argument[Qualifier];ReturnValue;taint;generated | | System.Xml.Serialization;ImportContext;false;ImportContext;(System.Xml.Serialization.CodeIdentifiers,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | @@ -8505,7 +8292,6 @@ | System.Xml.Serialization;XmlSerializationWriter;false;WriteElementStringRaw;(System.String,System.String,System.String);;Argument[2];Argument[Qualifier];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WriteElementStringRaw;(System.String,System.String,System.String,System.Xml.XmlQualifiedName);;Argument[2];Argument[Qualifier];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WriteElementStringRaw;(System.String,System.String,System.Xml.XmlQualifiedName);;Argument[1];Argument[Qualifier];taint;generated | -| System.Xml.Serialization;XmlSerializationWriter;false;WriteId;(System.Object);;Argument[Qualifier];Argument[0];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WriteNullableStringEncoded;(System.String,System.String,System.String,System.Xml.XmlQualifiedName);;Argument[2];Argument[Qualifier];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WriteNullableStringEncodedRaw;(System.String,System.String,System.Byte[],System.Xml.XmlQualifiedName);;Argument[2].Element;Argument[Qualifier];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WriteNullableStringEncodedRaw;(System.String,System.String,System.String,System.Xml.XmlQualifiedName);;Argument[2];Argument[Qualifier];taint;generated | @@ -8513,15 +8299,9 @@ | System.Xml.Serialization;XmlSerializationWriter;false;WriteNullableStringLiteralRaw;(System.String,System.String,System.Byte[]);;Argument[2].Element;Argument[Qualifier];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WriteNullableStringLiteralRaw;(System.String,System.String,System.String);;Argument[2];Argument[Qualifier];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object);;Argument[2];Argument[Qualifier];taint;generated | -| System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object);;Argument[Qualifier];Argument[2];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object,System.Type);;Argument[2];Argument[Qualifier];taint;generated | -| System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object,System.Type);;Argument[Qualifier];Argument[2];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object,System.Type,System.Boolean);;Argument[2];Argument[Qualifier];taint;generated | -| System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object,System.Type,System.Boolean);;Argument[Qualifier];Argument[2];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object,System.Type,System.Boolean,System.Boolean);;Argument[2];Argument[Qualifier];taint;generated | -| System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object,System.Type,System.Boolean,System.Boolean);;Argument[Qualifier];Argument[2];taint;generated | -| System.Xml.Serialization;XmlSerializationWriter;false;WriteReferencingElement;(System.String,System.String,System.Object);;Argument[Qualifier];Argument[2];taint;generated | -| System.Xml.Serialization;XmlSerializationWriter;false;WriteReferencingElement;(System.String,System.String,System.Object,System.Boolean);;Argument[Qualifier];Argument[2];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WriteRpcResult;(System.String,System.String);;Argument[0];Argument[Qualifier];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WriteRpcResult;(System.String,System.String);;Argument[1];Argument[Qualifier];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WriteSerializable;(System.Xml.Serialization.IXmlSerializable,System.String,System.String,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | @@ -10357,27 +10137,6 @@ | System;TupleExtensions;false;Deconstruct<,>;(System.Tuple,T1,T2);;Argument[0].Property[System.Tuple<,>.Item1];Argument[1];value;manual | | System;TupleExtensions;false;Deconstruct<,>;(System.Tuple,T1,T2);;Argument[0].Property[System.Tuple<,>.Item2];Argument[2];value;manual | | System;TupleExtensions;false;Deconstruct<>;(System.Tuple,T1);;Argument[0].Property[System.Tuple<>.Item1];Argument[1];value;manual | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated | | System;TupleExtensions;false;ToValueTuple<,,,,,,,,,,,,,,,,,,,,>;(System.Tuple>>);;Argument[0];ReturnValue;taint;generated | | System;TupleExtensions;false;ToValueTuple<,,,,,,,,,,,,,,,,,,,>;(System.Tuple>>);;Argument[0];ReturnValue;taint;generated | | System;TupleExtensions;false;ToValueTuple<,,,,,,,,,,,,,,,,,,>;(System.Tuple>>);;Argument[0];ReturnValue;taint;generated | diff --git a/csharp/ql/test/library-tests/dataflow/library/FlowSummariesFiltered.expected b/csharp/ql/test/library-tests/dataflow/library/FlowSummariesFiltered.expected index 1a2f70f48e8..a77b56ab64b 100644 --- a/csharp/ql/test/library-tests/dataflow/library/FlowSummariesFiltered.expected +++ b/csharp/ql/test/library-tests/dataflow/library/FlowSummariesFiltered.expected @@ -255,7 +255,6 @@ | System.Collections.Concurrent;ConcurrentDictionary<,>;false;ConcurrentDictionary;(System.Collections.Generic.IEnumerable>,System.Collections.Generic.IEqualityComparer);;Argument[0].Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];ReturnValue.Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];value;manual | | System.Collections.Concurrent;ConcurrentDictionary<,>;false;ConcurrentDictionary;(System.Int32,System.Collections.Generic.IEnumerable>,System.Collections.Generic.IEqualityComparer);;Argument[1].Element.Property[System.Collections.Generic.KeyValuePair<,>.Key];ReturnValue.Element.Property[System.Collections.Generic.KeyValuePair<,>.Key];value;manual | | System.Collections.Concurrent;ConcurrentDictionary<,>;false;ConcurrentDictionary;(System.Int32,System.Collections.Generic.IEnumerable>,System.Collections.Generic.IEqualityComparer);;Argument[1].Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];ReturnValue.Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];value;manual | -| System.Collections.Concurrent;ConcurrentDictionary<,>;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Concurrent;ConcurrentDictionary<,>;false;GetOrAdd;(TKey,TValue);;Argument[1];ReturnValue;taint;generated | | System.Collections.Concurrent;ConcurrentDictionary<,>;false;get_Keys;();;Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Key];ReturnValue.Element;value;manual | | System.Collections.Concurrent;ConcurrentDictionary<,>;false;get_Values;();;Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];ReturnValue.Element;value;manual | @@ -275,9 +274,6 @@ | System.Collections.Generic;CollectionExtensions;false;GetValueOrDefault<,>;(System.Collections.Generic.IReadOnlyDictionary,TKey,TValue);;Argument[1];ReturnValue;taint;generated | | System.Collections.Generic;CollectionExtensions;false;GetValueOrDefault<,>;(System.Collections.Generic.IReadOnlyDictionary,TKey,TValue);;Argument[2];ReturnValue;taint;generated | | System.Collections.Generic;CollectionExtensions;false;Remove<,>;(System.Collections.Generic.IDictionary,TKey,TValue);;Argument[0].Element;ReturnValue;taint;generated | -| System.Collections.Generic;CollectionExtensions;false;TryAdd<,>;(System.Collections.Generic.IDictionary,TKey,TValue);;Argument[0].Element;Argument[2];taint;generated | -| System.Collections.Generic;CollectionExtensions;false;TryAdd<,>;(System.Collections.Generic.IDictionary,TKey,TValue);;Argument[1];Argument[0].Element;taint;generated | -| System.Collections.Generic;CollectionExtensions;false;TryAdd<,>;(System.Collections.Generic.IDictionary,TKey,TValue);;Argument[2];Argument[0].Element;taint;generated | | System.Collections.Generic;Dictionary<,>+Enumerator;false;get_Current;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Generic;Dictionary<,>+Enumerator;false;get_Entry;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Generic;Dictionary<,>+Enumerator;false;get_Key;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -327,8 +323,6 @@ | System.Collections.Generic;IList<>;true;get_Item;(System.Int32);;Argument[Qualifier].Element;ReturnValue;value;manual | | System.Collections.Generic;IList<>;true;set_Item;(System.Int32,T);;Argument[1];Argument[Qualifier].Element;value;manual | | System.Collections.Generic;ISet<>;true;Add;(T);;Argument[0];Argument[Qualifier].Element;value;manual | -| System.Collections.Generic;KeyValuePair;false;Create<,>;(TKey,TValue);;Argument[0];ReturnValue;taint;generated | -| System.Collections.Generic;KeyValuePair;false;Create<,>;(TKey,TValue);;Argument[1];ReturnValue;taint;generated | | System.Collections.Generic;KeyValuePair<,>;false;Deconstruct;(TKey,TValue);;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Generic;KeyValuePair<,>;false;KeyValuePair;(TKey,TValue);;Argument[0];ReturnValue.Property[System.Collections.Generic.KeyValuePair<,>.Key];value;manual | | System.Collections.Generic;KeyValuePair<,>;false;KeyValuePair;(TKey,TValue);;Argument[1];ReturnValue.Property[System.Collections.Generic.KeyValuePair<,>.Value];value;manual | @@ -518,12 +512,6 @@ | System.Collections.Immutable;ImmutableDictionary;false;Create<,>;(System.Collections.Generic.IEqualityComparer);;Argument[0];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableDictionary;false;Create<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEqualityComparer);;Argument[0];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableDictionary;false;Create<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEqualityComparer);;Argument[1];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEnumerable>);;Argument[0].Element;ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[0];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[1].Element;ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[0];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[1];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[2].Element;ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableDictionary;false;GetValueOrDefault<,>;(System.Collections.Immutable.IImmutableDictionary,TKey,TValue);;Argument[2];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableDictionary;false;ToImmutableDictionary<,>;(System.Collections.Generic.IEnumerable>);;Argument[0].Element;ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableDictionary;false;ToImmutableDictionary<,>;(System.Collections.Generic.IEnumerable>,System.Collections.Generic.IEqualityComparer);;Argument[0].Element;ReturnValue;taint;generated | @@ -567,8 +555,6 @@ | System.Collections.Immutable;ImmutableDictionary<,>;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated | | System.Collections.Immutable;ImmutableDictionary<,>;false;get_ValueComparer;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableDictionary<,>;false;get_Values;();;Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];ReturnValue.Element;value;manual | -| System.Collections.Immutable;ImmutableHashSet;false;Create<>;(System.Collections.Generic.IEqualityComparer,T);;Argument[1];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableHashSet;false;Create<>;(T);;Argument[0];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableHashSet;false;CreateRange<>;(System.Collections.Generic.IEnumerable);;Argument[0].Element;ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableHashSet;false;CreateRange<>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable);;Argument[1].Element;ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableHashSet;false;ToImmutableHashSet<>;(System.Collections.Generic.IEnumerable);;Argument[0].Element;ReturnValue;taint;generated | @@ -593,9 +579,6 @@ | System.Collections.Immutable;ImmutableHashSet<>;false;get_KeyComparer;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableHashSet<>;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated | | System.Collections.Immutable;ImmutableInterlocked;false;GetOrAdd<,>;(System.Collections.Immutable.ImmutableDictionary,TKey,TValue);;Argument[2];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableList;false;Create<>;(T);;Argument[0];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableList;false;Create<>;(T[]);;Argument[0].Element;ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableList;false;CreateRange<>;(System.Collections.Generic.IEnumerable);;Argument[0].Element;ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableList;false;Remove<>;(System.Collections.Immutable.IImmutableList,T);;Argument[0].Element;ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableList;false;RemoveRange<>;(System.Collections.Immutable.IImmutableList,System.Collections.Generic.IEnumerable);;Argument[0].Element;ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableList;false;Replace<>;(System.Collections.Immutable.IImmutableList,T,T);;Argument[0].Element;ReturnValue;taint;generated | @@ -681,12 +664,6 @@ | System.Collections.Immutable;ImmutableSortedDictionary;false;CreateBuilder<,>;(System.Collections.Generic.IComparer);;Argument[0];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedDictionary;false;CreateBuilder<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEqualityComparer);;Argument[0];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedDictionary;false;CreateBuilder<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEqualityComparer);;Argument[1];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEnumerable>);;Argument[0];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEnumerable>);;Argument[1].Element;ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[0];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[1];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[2].Element;ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IEnumerable>);;Argument[0].Element;ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedDictionary;false;ToImmutableSortedDictionary<,>;(System.Collections.Generic.IEnumerable>);;Argument[0].Element;ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedDictionary;false;ToImmutableSortedDictionary<,>;(System.Collections.Generic.IEnumerable>,System.Collections.Generic.IComparer);;Argument[0].Element;ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedDictionary;false;ToImmutableSortedDictionary<,>;(System.Collections.Generic.IEnumerable>,System.Collections.Generic.IComparer);;Argument[1];ReturnValue;taint;generated | @@ -709,7 +686,6 @@ | System.Collections.Immutable;ImmutableSortedDictionary<,>+Builder;false;get_Values;();;Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];ReturnValue.Element;value;manual | | System.Collections.Immutable;ImmutableSortedDictionary<,>+Builder;false;set_KeyComparer;(System.Collections.Generic.IComparer);;Argument[0];Argument[Qualifier];taint;generated | | System.Collections.Immutable;ImmutableSortedDictionary<,>+Builder;false;set_ValueComparer;(System.Collections.Generic.IEqualityComparer);;Argument[0];Argument[Qualifier];taint;generated | -| System.Collections.Immutable;ImmutableSortedDictionary<,>+Enumerator;false;get_Current;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedDictionary<,>;false;Add;(System.Collections.Generic.KeyValuePair);;Argument[0].Property[System.Collections.Generic.KeyValuePair<,>.Key];Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Key];value;manual | | System.Collections.Immutable;ImmutableSortedDictionary<,>;false;Add;(System.Collections.Generic.KeyValuePair);;Argument[0].Property[System.Collections.Generic.KeyValuePair<,>.Value];Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];value;manual | | System.Collections.Immutable;ImmutableSortedDictionary<,>;false;Add;(TKey,TValue);;Argument[0];Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Key];value;manual | @@ -736,10 +712,7 @@ | System.Collections.Immutable;ImmutableSortedDictionary<,>;false;get_ValueComparer;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedDictionary<,>;false;get_Values;();;Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];ReturnValue.Element;value;manual | | System.Collections.Immutable;ImmutableSortedSet;false;Create<>;(System.Collections.Generic.IComparer);;Argument[0];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableSortedSet;false;Create<>;(System.Collections.Generic.IComparer,T);;Argument[0];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableSortedSet;false;Create<>;(System.Collections.Generic.IComparer,T);;Argument[1];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet;false;Create<>;(System.Collections.Generic.IComparer,T[]);;Argument[0];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableSortedSet;false;Create<>;(T);;Argument[0];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet;false;CreateBuilder<>;(System.Collections.Generic.IComparer);;Argument[0];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet;false;CreateRange<>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEnumerable);;Argument[0];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet;false;CreateRange<>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEnumerable);;Argument[1].Element;ReturnValue;taint;generated | @@ -751,7 +724,6 @@ | System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;GetEnumerator;();;Argument[Qualifier].Element;ReturnValue.Property[System.Collections.Immutable.ImmutableSortedSet<>+Enumerator.Current];value;manual | | System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;IntersectWith;(System.Collections.Generic.IEnumerable);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;Reverse;();;Argument[0].Element;ReturnValue.Element;value;manual | -| System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;SymmetricExceptWith;(System.Collections.Generic.IEnumerable);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;ToImmutable;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;TryGetValue;(T,T);;Argument[0];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;TryGetValue;(T,T);;Argument[Qualifier];ReturnValue;taint;generated | @@ -765,12 +737,8 @@ | System.Collections.Immutable;ImmutableSortedSet<>;false;Add;(T);;Argument[0];Argument[Qualifier].Element;value;manual | | System.Collections.Immutable;ImmutableSortedSet<>;false;Except;(System.Collections.Generic.IEnumerable);;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet<>;false;GetEnumerator;();;Argument[Qualifier].Element;ReturnValue.Property[System.Collections.Immutable.ImmutableSortedSet<>+Enumerator.Current];value;manual | -| System.Collections.Immutable;ImmutableSortedSet<>;false;Intersect;(System.Collections.Generic.IEnumerable);;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet<>;false;Remove;(T);;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet<>;false;Reverse;();;Argument[0].Element;ReturnValue.Element;value;manual | -| System.Collections.Immutable;ImmutableSortedSet<>;false;SymmetricExcept;(System.Collections.Generic.IEnumerable);;Argument[0].Element;Argument[Qualifier];taint;generated | -| System.Collections.Immutable;ImmutableSortedSet<>;false;SymmetricExcept;(System.Collections.Generic.IEnumerable);;Argument[0].Element;ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableSortedSet<>;false;SymmetricExcept;(System.Collections.Generic.IEnumerable);;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet<>;false;ToBuilder;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet<>;false;TryGetValue;(T,T);;Argument[0];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet<>;false;TryGetValue;(T,T);;Argument[Qualifier];ReturnValue;taint;generated | @@ -796,24 +764,18 @@ | System.Collections.Immutable;ImmutableStack<>;false;Push;(T);;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.ObjectModel;Collection<>;false;Collection;(System.Collections.Generic.IList);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.Collections.ObjectModel;Collection<>;false;InsertItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated | -| System.Collections.ObjectModel;Collection<>;false;InsertItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated | | System.Collections.ObjectModel;Collection<>;false;SetItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated | -| System.Collections.ObjectModel;Collection<>;false;SetItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated | | System.Collections.ObjectModel;Collection<>;false;get_Items;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.ObjectModel;Collection<>;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.ObjectModel;KeyedCollection<,>;false;InsertItem;(System.Int32,TItem);;Argument[1];Argument[Qualifier];taint;generated | -| System.Collections.ObjectModel;KeyedCollection<,>;false;InsertItem;(System.Int32,TItem);;Argument[Qualifier];Argument[1];taint;generated | | System.Collections.ObjectModel;KeyedCollection<,>;false;KeyedCollection;(System.Collections.Generic.IEqualityComparer,System.Int32);;Argument[0];Argument[Qualifier];taint;generated | | System.Collections.ObjectModel;KeyedCollection<,>;false;SetItem;(System.Int32,TItem);;Argument[1];Argument[Qualifier];taint;generated | -| System.Collections.ObjectModel;KeyedCollection<,>;false;SetItem;(System.Int32,TItem);;Argument[Qualifier];Argument[1];taint;generated | | System.Collections.ObjectModel;KeyedCollection<,>;false;TryGetValue;(TKey,TItem);;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.ObjectModel;KeyedCollection<,>;false;get_Comparer;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.ObjectModel;KeyedCollection<,>;false;get_Dictionary;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.ObjectModel;KeyedCollection<,>;false;get_Item;(TKey);;Argument[Qualifier].Element;ReturnValue;value;manual | | System.Collections.ObjectModel;ObservableCollection<>;false;InsertItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated | -| System.Collections.ObjectModel;ObservableCollection<>;false;InsertItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated | | System.Collections.ObjectModel;ObservableCollection<>;false;SetItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated | -| System.Collections.ObjectModel;ObservableCollection<>;false;SetItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated | | System.Collections.ObjectModel;ReadOnlyCollection<>;false;ReadOnlyCollection;(System.Collections.Generic.IList);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.Collections.ObjectModel;ReadOnlyCollection<>;false;get_Item;(System.Int32);;Argument[Qualifier].Element;ReturnValue;value;manual | | System.Collections.ObjectModel;ReadOnlyCollection<>;false;get_Items;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -924,7 +886,6 @@ | System.Collections;BitArray;false;Xor;(System.Collections.BitArray);;Argument[Qualifier];ReturnValue;value;generated | | System.Collections;BitArray;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated | | System.Collections;CollectionBase;false;Remove;(System.Object);;Argument[0];Argument[Qualifier];taint;generated | -| System.Collections;CollectionBase;false;Remove;(System.Object);;Argument[Qualifier];Argument[0];taint;generated | | System.Collections;CollectionBase;false;get_InnerList;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections;CollectionBase;false;get_List;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections;CollectionBase;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -1101,9 +1062,7 @@ | System.ComponentModel;BaseNumberConverter;false;ConvertTo;(System.ComponentModel.ITypeDescriptorContext,System.Globalization.CultureInfo,System.Object,System.Type);;Argument[1];ReturnValue;taint;generated | | System.ComponentModel;BaseNumberConverter;false;ConvertTo;(System.ComponentModel.ITypeDescriptorContext,System.Globalization.CultureInfo,System.Object,System.Type);;Argument[2];ReturnValue;taint;generated | | System.ComponentModel;BindingList<>;false;InsertItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated | -| System.ComponentModel;BindingList<>;false;InsertItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated | | System.ComponentModel;BindingList<>;false;SetItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated | -| System.ComponentModel;BindingList<>;false;SetItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated | | System.ComponentModel;CategoryAttribute;false;CategoryAttribute;(System.String);;Argument[0];Argument[Qualifier];taint;generated | | System.ComponentModel;CategoryAttribute;false;get_Category;();;Argument[Qualifier];ReturnValue;taint;generated | | System.ComponentModel;CharConverter;false;ConvertTo;(System.ComponentModel.ITypeDescriptorContext,System.Globalization.CultureInfo,System.Object,System.Type);;Argument[2];ReturnValue;taint;generated | @@ -1388,10 +1347,6 @@ | System.Data.Common;DataTableMappingCollection;false;set_Item;(System.String,System.Data.Common.DataTableMapping);;Argument[1];Argument[Qualifier].Element;value;manual | | System.Data.Common;DbCommand;false;ExecuteReader;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbCommand;false;ExecuteReader;(System.Data.CommandBehavior);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Data.Common;DbCommand;false;ExecuteReaderAsync;();;Argument[Qualifier];ReturnValue;taint;generated | -| System.Data.Common;DbCommand;false;ExecuteReaderAsync;(System.Data.CommandBehavior);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Data.Common;DbCommand;false;ExecuteReaderAsync;(System.Data.CommandBehavior,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Data.Common;DbCommand;false;ExecuteReaderAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbCommand;false;get_Connection;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbCommand;false;get_Parameters;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbCommand;false;get_Transaction;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -1399,7 +1354,6 @@ | System.Data.Common;DbCommand;false;set_Connection;(System.Data.IDbConnection);;Argument[0];Argument[Qualifier];taint;generated | | System.Data.Common;DbCommand;false;set_Transaction;(System.Data.Common.DbTransaction);;Argument[0];Argument[Qualifier];taint;generated | | System.Data.Common;DbCommand;false;set_Transaction;(System.Data.IDbTransaction);;Argument[0];Argument[Qualifier];taint;generated | -| System.Data.Common;DbCommand;true;ExecuteDbDataReaderAsync;(System.Data.CommandBehavior,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbCommand;true;PrepareAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Data.Common;DbCommandBuilder;false;GetDeleteCommand;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbCommandBuilder;false;GetDeleteCommand;(System.Boolean);;Argument[Qualifier];ReturnValue;taint;generated | @@ -1428,7 +1382,6 @@ | System.Data.Common;DbConnectionStringBuilder;false;AppendKeyValuePair;(System.Text.StringBuilder,System.String,System.String);;Argument[2];Argument[0];taint;generated | | System.Data.Common;DbConnectionStringBuilder;false;AppendKeyValuePair;(System.Text.StringBuilder,System.String,System.String,System.Boolean);;Argument[1];Argument[0];taint;generated | | System.Data.Common;DbConnectionStringBuilder;false;AppendKeyValuePair;(System.Text.StringBuilder,System.String,System.String,System.Boolean);;Argument[2];Argument[0];taint;generated | -| System.Data.Common;DbConnectionStringBuilder;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbConnectionStringBuilder;false;GetProperties;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbConnectionStringBuilder;false;GetProperties;(System.Attribute[]);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbConnectionStringBuilder;false;GetPropertyOwner;(System.ComponentModel.PropertyDescriptor);;Argument[Qualifier];ReturnValue;value;generated | @@ -1458,12 +1411,9 @@ | System.Data.Common;DbDataAdapter;true;CreateRowUpdatingEvent;(System.Data.DataRow,System.Data.IDbCommand,System.Data.StatementType,System.Data.Common.DataTableMapping);;Argument[0];ReturnValue;taint;generated | | System.Data.Common;DbDataAdapter;true;CreateRowUpdatingEvent;(System.Data.DataRow,System.Data.IDbCommand,System.Data.StatementType,System.Data.Common.DataTableMapping);;Argument[1];ReturnValue;taint;generated | | System.Data.Common;DbDataAdapter;true;CreateRowUpdatingEvent;(System.Data.DataRow,System.Data.IDbCommand,System.Data.StatementType,System.Data.Common.DataTableMapping);;Argument[3];ReturnValue;taint;generated | -| System.Data.Common;DbDataReader;false;GetFieldValueAsync<>;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbDataReader;true;GetFieldValue<>;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Data.Common;DbDataReader;true;GetFieldValueAsync<>;(System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbDataReader;true;GetProviderSpecificValue;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbDataReader;true;GetProviderSpecificValues;(System.Object[]);;Argument[Qualifier];Argument[0].Element;taint;generated | -| System.Data.Common;DbDataReader;true;GetSchemaTableAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbDataReader;true;GetTextReader;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbDataRecord;false;GetPropertyOwner;(System.ComponentModel.PropertyDescriptor);;Argument[Qualifier];ReturnValue;value;generated | | System.Data.Common;DbEnumerator;false;DbEnumerator;(System.Data.IDataReader);;Argument[0];Argument[Qualifier];taint;generated | @@ -1598,11 +1548,8 @@ | System.Data;DataColumn;false;set_Prefix;(System.String);;Argument[0];Argument[Qualifier];taint;generated | | System.Data;DataColumnChangeEventArgs;false;DataColumnChangeEventArgs;(System.Data.DataRow,System.Data.DataColumn,System.Object);;Argument[1];Argument[Qualifier];taint;generated | | System.Data;DataColumnChangeEventArgs;false;get_Column;();;Argument[Qualifier];ReturnValue;taint;generated | -| System.Data;DataColumnCollection;false;Add;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Data;DataColumnCollection;false;Add;(System.Data.DataColumn);;Argument[0];Argument[Qualifier].Element;value;manual | | System.Data;DataColumnCollection;false;Add;(System.String);;Argument[0];Argument[Qualifier].Element;value;manual | -| System.Data;DataColumnCollection;false;Add;(System.String,System.Type);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Data;DataColumnCollection;false;Add;(System.String,System.Type,System.String);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data;DataColumnCollection;false;AddRange;(System.Data.DataColumn[]);;Argument[0].Element;Argument[Qualifier].Element;value;manual | | System.Data;DataColumnCollection;false;CopyTo;(System.Data.DataColumn[],System.Int32);;Argument[Qualifier].Element;Argument[0].Element;value;manual | | System.Data;DataColumnCollection;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | @@ -1610,7 +1557,6 @@ | System.Data;DataColumnCollection;false;get_List;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Data;DataReaderExtensions;false;GetDateTime;(System.Data.Common.DbDataReader,System.String);;Argument[0].Element;ReturnValue;taint;generated | | System.Data;DataReaderExtensions;false;GetFieldValue<>;(System.Data.Common.DbDataReader,System.String);;Argument[0].Element;ReturnValue;taint;generated | -| System.Data;DataReaderExtensions;false;GetFieldValueAsync<>;(System.Data.Common.DbDataReader,System.String,System.Threading.CancellationToken);;Argument[0].Element;ReturnValue;taint;generated | | System.Data;DataReaderExtensions;false;GetGuid;(System.Data.Common.DbDataReader,System.String);;Argument[0].Element;ReturnValue;taint;generated | | System.Data;DataReaderExtensions;false;GetProviderSpecificValue;(System.Data.Common.DbDataReader,System.String);;Argument[0].Element;ReturnValue;taint;generated | | System.Data;DataReaderExtensions;false;GetString;(System.Data.Common.DbDataReader,System.String);;Argument[0].Element;ReturnValue;taint;generated | @@ -1642,16 +1588,10 @@ | System.Data;DataRelationCollection;false;Add;(System.Data.DataRelation);;Argument[0];Argument[Qualifier].Element;value;manual | | System.Data;DataRelationCollection;false;CopyTo;(System.Data.DataRelation[],System.Int32);;Argument[Qualifier].Element;Argument[0].Element;value;manual | | System.Data;DataRelationCollection;false;Remove;(System.Data.DataRelation);;Argument[0];Argument[Qualifier];taint;generated | -| System.Data;DataRelationCollection;true;Add;(System.Data.DataColumn,System.Data.DataColumn);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Data;DataRelationCollection;true;Add;(System.Data.DataColumn[],System.Data.DataColumn[]);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn,System.Data.DataColumn);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn,System.Data.DataColumn,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | | System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn,System.Data.DataColumn,System.Boolean);;Argument[0];ReturnValue;taint;generated | -| System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn,System.Data.DataColumn,System.Boolean);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn[],System.Data.DataColumn[]);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn[],System.Data.DataColumn[],System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | | System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn[],System.Data.DataColumn[],System.Boolean);;Argument[0];ReturnValue;taint;generated | -| System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn[],System.Data.DataColumn[],System.Boolean);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data;DataRelationCollection;true;AddRange;(System.Data.DataRelation[]);;Argument[0].Element;Argument[Qualifier].Element;value;manual | | System.Data;DataRow;false;DataRow;(System.Data.DataRowBuilder);;Argument[0];Argument[Qualifier];taint;generated | | System.Data;DataRow;false;GetChildRows;(System.Data.DataRelation);;Argument[Qualifier];ReturnValue;taint;generated | @@ -1755,12 +1695,10 @@ | System.Data;DataTable;false;set_PrimaryKey;(System.Data.DataColumn[]);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.Data;DataTable;false;set_Site;(System.ComponentModel.ISite);;Argument[0];Argument[Qualifier];taint;generated | | System.Data;DataTable;false;set_TableName;(System.String);;Argument[0];Argument[Qualifier];taint;generated | -| System.Data;DataTableCollection;false;Add;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Data;DataTableCollection;false;Add;(System.Data.DataTable);;Argument[0];Argument[Qualifier].Element;value;manual | | System.Data;DataTableCollection;false;Add;(System.String);;Argument[0];Argument[Qualifier].Element;value;manual | | System.Data;DataTableCollection;false;Add;(System.String,System.String);;Argument[1];Argument[Qualifier];taint;generated | | System.Data;DataTableCollection;false;Add;(System.String,System.String);;Argument[1];ReturnValue;taint;generated | -| System.Data;DataTableCollection;false;Add;(System.String,System.String);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data;DataTableCollection;false;AddRange;(System.Data.DataTable[]);;Argument[0].Element;Argument[Qualifier].Element;value;manual | | System.Data;DataTableCollection;false;CopyTo;(System.Data.DataTable[],System.Int32);;Argument[Qualifier].Element;Argument[0].Element;value;manual | | System.Data;DataTableCollection;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | @@ -2341,8 +2279,10 @@ | System.IO.IsolatedStorage;IsolatedStorage;false;get_DomainIdentity;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO.IsolatedStorage;IsolatedStorageFileStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.IO.IsolatedStorage;IsolatedStorageFileStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | +| System.IO.IsolatedStorage;IsolatedStorageFileStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO.IsolatedStorage;IsolatedStorageFileStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.IO.IsolatedStorage;IsolatedStorageFileStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | +| System.IO.IsolatedStorage;IsolatedStorageFileStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO.MemoryMappedFiles;MemoryMappedFile;false;CreateFromFile;(System.IO.FileStream,System.String,System.Int64,System.IO.MemoryMappedFiles.MemoryMappedFileAccess,System.IO.HandleInheritability,System.Boolean);;Argument[0];ReturnValue;taint;generated | | System.IO.MemoryMappedFiles;MemoryMappedFile;false;get_SafeMemoryMappedFileHandle;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO.MemoryMappedFiles;MemoryMappedViewAccessor;false;get_SafeMemoryMappedViewHandle;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -2367,7 +2307,6 @@ | System.IO;BinaryReader;false;get_BaseStream;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;BinaryWriter;false;BinaryWriter;(System.IO.Stream,System.Text.Encoding,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;BinaryWriter;false;BinaryWriter;(System.IO.Stream,System.Text.Encoding,System.Boolean);;Argument[1];Argument[Qualifier];taint;generated | -| System.IO;BinaryWriter;false;DisposeAsync;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;BinaryWriter;false;Write;(System.Byte[]);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.IO;BinaryWriter;false;Write;(System.Byte[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.IO;BinaryWriter;false;get_BaseStream;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -2427,8 +2366,10 @@ | System.IO;FileNotFoundException;false;get_Message;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;FileStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.IO;FileStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | +| System.IO;FileStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;FileStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.IO;FileStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | +| System.IO;FileStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;FileStream;false;get_SafeFileHandle;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;FileSystemEventArgs;false;FileSystemEventArgs;(System.IO.WatcherChangeTypes,System.String,System.String);;Argument[1];Argument[Qualifier];taint;generated | | System.IO;FileSystemEventArgs;false;FileSystemEventArgs;(System.IO.WatcherChangeTypes,System.String,System.String);;Argument[2];Argument[Qualifier];taint;generated | @@ -2501,7 +2442,6 @@ | System.IO;Stream;false;CopyToAsync;(System.IO.Stream);;Argument[Qualifier];Argument[0];taint;manual | | System.IO;Stream;false;CopyToAsync;(System.IO.Stream,System.Int32);;Argument[Qualifier];Argument[0];taint;manual | | System.IO;Stream;false;CopyToAsync;(System.IO.Stream,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;manual | -| System.IO;Stream;false;FlushAsync;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;Stream;false;ReadAsync;(System.Byte[],System.Int32,System.Int32);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.IO;Stream;false;Synchronized;(System.IO.Stream);;Argument[0];ReturnValue;taint;generated | | System.IO;Stream;false;WriteAsync;(System.Byte[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;manual | @@ -2509,13 +2449,10 @@ | System.IO;Stream;true;BeginWrite;(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object);;Argument[0].Element;Argument[Qualifier];taint;manual | | System.IO;Stream;true;CopyTo;(System.IO.Stream,System.Int32);;Argument[Qualifier];Argument[0];taint;manual | | System.IO;Stream;true;CopyToAsync;(System.IO.Stream,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;manual | -| System.IO;Stream;true;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;Stream;true;Read;(System.Byte[],System.Int32,System.Int32);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.IO;Stream;true;ReadAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0].Element;taint;manual | -| System.IO;Stream;true;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;Stream;true;Write;(System.Byte[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;manual | | System.IO;Stream;true;WriteAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[0].Element;Argument[Qualifier];taint;manual | -| System.IO;Stream;true;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;StreamReader;false;StreamReader;(System.IO.Stream,System.Text.Encoding,System.Boolean,System.Int32,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;StreamReader;false;StreamReader;(System.IO.Stream,System.Text.Encoding,System.Boolean,System.Int32,System.Boolean);;Argument[1];Argument[Qualifier];taint;generated | | System.IO;StreamReader;false;get_BaseStream;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -2530,13 +2467,10 @@ | System.IO;StringWriter;false;ToString;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;StringWriter;false;Write;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.IO;StringWriter;false;Write;(System.String);;Argument[0];Argument[Qualifier];taint;generated | -| System.IO;StringWriter;false;Write;(System.Text.StringBuilder);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;StringWriter;false;WriteAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.IO;StringWriter;false;WriteAsync;(System.String);;Argument[0];Argument[Qualifier];taint;generated | -| System.IO;StringWriter;false;WriteAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;StringWriter;false;WriteLineAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.IO;StringWriter;false;WriteLineAsync;(System.String);;Argument[0];Argument[Qualifier];taint;generated | -| System.IO;StringWriter;false;WriteLineAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;TextReader;false;Synchronized;(System.IO.TextReader);;Argument[0];ReturnValue;taint;generated | | System.IO;TextReader;true;Read;();;Argument[Qualifier];ReturnValue;taint;manual | | System.IO;TextReader;true;Read;(System.Char[],System.Int32,System.Int32);;Argument[Qualifier];ReturnValue;taint;manual | @@ -2554,12 +2488,7 @@ | System.IO;TextWriter;false;Synchronized;(System.IO.TextWriter);;Argument[0];ReturnValue;taint;generated | | System.IO;TextWriter;false;TextWriter;(System.IFormatProvider);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;TextWriter;false;WriteAsync;(System.Char[]);;Argument[0].Element;Argument[Qualifier];taint;generated | -| System.IO;TextWriter;false;WriteAsync;(System.Char[]);;Argument[0].Element;ReturnValue;taint;generated | -| System.IO;TextWriter;false;WriteAsync;(System.Char[]);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;TextWriter;false;WriteLineAsync;(System.Char[]);;Argument[0].Element;Argument[Qualifier];taint;generated | -| System.IO;TextWriter;false;WriteLineAsync;(System.Char[]);;Argument[0].Element;ReturnValue;taint;generated | -| System.IO;TextWriter;false;WriteLineAsync;(System.Char[]);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;TextWriter;true;FlushAsync;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;TextWriter;true;Write;(System.Char[]);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.IO;TextWriter;true;Write;(System.Object);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;TextWriter;true;Write;(System.String,System.Object);;Argument[0];Argument[Qualifier];taint;generated | @@ -2573,14 +2502,7 @@ | System.IO;TextWriter;true;Write;(System.String,System.Object,System.Object,System.Object);;Argument[3];Argument[Qualifier];taint;generated | | System.IO;TextWriter;true;Write;(System.String,System.Object[]);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;TextWriter;true;Write;(System.String,System.Object[]);;Argument[1].Element;Argument[Qualifier];taint;generated | -| System.IO;TextWriter;true;WriteAsync;(System.Char);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteAsync;(System.Char[],System.Int32,System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.IO;TextWriter;true;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteAsync;(System.String);;Argument[0];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteAsync;(System.String);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;TextWriter;true;WriteAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | | System.IO;TextWriter;true;WriteLine;(System.Char[]);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.IO;TextWriter;true;WriteLine;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated | @@ -2597,18 +2519,8 @@ | System.IO;TextWriter;true;WriteLine;(System.String,System.Object,System.Object,System.Object);;Argument[3];Argument[Qualifier];taint;generated | | System.IO;TextWriter;true;WriteLine;(System.String,System.Object[]);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;TextWriter;true;WriteLine;(System.String,System.Object[]);;Argument[1].Element;Argument[Qualifier];taint;generated | -| System.IO;TextWriter;true;WriteLine;(System.Text.StringBuilder);;Argument[0];Argument[Qualifier];taint;generated | -| System.IO;TextWriter;true;WriteLineAsync;();;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteLineAsync;(System.Char);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteLineAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteLineAsync;(System.Char[],System.Int32,System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.IO;TextWriter;true;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteLineAsync;(System.String);;Argument[0];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteLineAsync;(System.String);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;TextWriter;true;WriteLineAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteLineAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;TextWriter;true;get_FormatProvider;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;TextWriter;true;get_NewLine;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;TextWriter;true;set_NewLine;(System.String);;Argument[0];Argument[Qualifier];taint;generated | @@ -3879,17 +3791,12 @@ | System.Net.Http.Json;JsonContent;false;Create<>;(T,System.Net.Http.Headers.MediaTypeHeaderValue,System.Text.Json.JsonSerializerOptions);;Argument[2];ReturnValue;taint;generated | | System.Net.Http;ByteArrayContent;false;ByteArrayContent;(System.Byte[]);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.Net.Http;ByteArrayContent;false;ByteArrayContent;(System.Byte[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated | -| System.Net.Http;ByteArrayContent;false;CreateContentReadStreamAsync;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;ByteArrayContent;false;SerializeToStream;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[0];ReturnValue;taint;generated | | System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[2];ReturnValue;taint;generated | | System.Net.Http;DelegatingHandler;false;DelegatingHandler;(System.Net.Http.HttpMessageHandler);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Http;DelegatingHandler;false;SendAsync;(System.Net.Http.HttpRequestMessage,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | | System.Net.Http;DelegatingHandler;false;get_InnerHandler;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;DelegatingHandler;false;set_InnerHandler;(System.Net.Http.HttpMessageHandler);;Argument[0];Argument[Qualifier];taint;generated | -| System.Net.Http;FormUrlEncodedContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[2];ReturnValue;taint;generated | | System.Net.Http;HttpClient;false;Send;(System.Net.Http.HttpRequestMessage);;Argument[Qualifier];Argument[0];taint;generated | | System.Net.Http;HttpClient;false;Send;(System.Net.Http.HttpRequestMessage,System.Net.Http.HttpCompletionOption);;Argument[Qualifier];Argument[0];taint;generated | | System.Net.Http;HttpClient;false;Send;(System.Net.Http.HttpRequestMessage,System.Net.Http.HttpCompletionOption,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated | @@ -3916,10 +3823,7 @@ | System.Net.Http;HttpContent;false;ReadAsStreamAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;HttpContent;false;get_Headers;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;HttpContent;true;CreateContentReadStream;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Net.Http;HttpContent;true;CreateContentReadStreamAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Net.Http;HttpContent;true;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Net.Http;HttpContent;true;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Http;HttpContent;true;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;HttpMessageInvoker;false;HttpMessageInvoker;(System.Net.Http.HttpMessageHandler,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Http;HttpMessageInvoker;false;SendAsync;(System.Net.Http.HttpRequestMessage,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | | System.Net.Http;HttpMethod;false;HttpMethod;(System.String);;Argument[0];Argument[Qualifier];taint;generated | @@ -3955,7 +3859,6 @@ | System.Net.Http;MultipartFormDataContent;false;Add;(System.Net.Http.HttpContent);;Argument[0];Argument[Qualifier].Element;value;manual | | System.Net.Http;MultipartFormDataContent;false;Add;(System.Net.Http.HttpContent,System.String);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Http;MultipartFormDataContent;false;Add;(System.Net.Http.HttpContent,System.String,System.String);;Argument[0];Argument[Qualifier];taint;generated | -| System.Net.Http;ReadOnlyMemoryContent;false;CreateContentReadStreamAsync;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;ReadOnlyMemoryContent;false;ReadOnlyMemoryContent;(System.ReadOnlyMemory);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Http;SocketsHttpConnectionContext;false;get_DnsEndPoint;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;SocketsHttpConnectionContext;false;get_InitialRequestMessage;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -3992,13 +3895,9 @@ | System.Net.Http;SocketsHttpPlaintextStreamFilterContext;false;get_NegotiatedHttpVersion;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;SocketsHttpPlaintextStreamFilterContext;false;get_PlaintextStream;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;StreamContent;false;SerializeToStream;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[0];ReturnValue;taint;generated | | System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[2];ReturnValue;taint;generated | | System.Net.Http;StreamContent;false;StreamContent;(System.IO.Stream);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Http;StreamContent;false;StreamContent;(System.IO.Stream,System.Int32);;Argument[0];Argument[Qualifier];taint;generated | -| System.Net.Http;StringContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[2];ReturnValue;taint;generated | | System.Net.Mail;AlternateView;false;CreateAlternateViewFromString;(System.String);;Argument[0];ReturnValue;taint;generated | | System.Net.Mail;AlternateView;false;CreateAlternateViewFromString;(System.String,System.Net.Mime.ContentType);;Argument[0];ReturnValue;taint;generated | | System.Net.Mail;AlternateView;false;CreateAlternateViewFromString;(System.String,System.Net.Mime.ContentType);;Argument[1];ReturnValue;taint;generated | @@ -4124,7 +4023,6 @@ | System.Net.NetworkInformation;PhysicalAddress;false;PhysicalAddress;(System.Byte[]);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.Net.NetworkInformation;UnicastIPAddressInformationCollection;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Security;AuthenticatedStream;false;AuthenticatedStream;(System.IO.Stream,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | -| System.Net.Security;AuthenticatedStream;false;DisposeAsync;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Security;AuthenticatedStream;false;get_InnerStream;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Security;NegotiateStream;false;AuthenticateAsClient;(System.Net.NetworkCredential,System.Security.Authentication.ExtendedProtection.ChannelBinding,System.String);;Argument[1];Argument[Qualifier];taint;generated | | System.Net.Security;NegotiateStream;false;AuthenticateAsClient;(System.Net.NetworkCredential,System.Security.Authentication.ExtendedProtection.ChannelBinding,System.String);;Argument[2];Argument[Qualifier];taint;generated | @@ -4145,8 +4043,10 @@ | System.Net.Security;NegotiateStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Net.Security;NegotiateStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Net.Security;NegotiateStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | +| System.Net.Security;NegotiateStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Security;NegotiateStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Net.Security;NegotiateStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | +| System.Net.Security;NegotiateStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Security;NegotiateStream;false;get_RemoteIdentity;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Security;SslApplicationProtocol;false;ToString;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Security;SslApplicationProtocol;false;get_Protocol;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -4173,7 +4073,9 @@ | System.Net.Sockets;NetworkStream;false;NetworkStream;(System.Net.Sockets.Socket,System.IO.FileAccess,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Sockets;NetworkStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Net.Sockets;NetworkStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | +| System.Net.Sockets;NetworkStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Sockets;NetworkStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | +| System.Net.Sockets;NetworkStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Sockets;NetworkStream;false;get_Socket;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Sockets;SafeSocketHandle;false;SafeSocketHandle;(System.IntPtr,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Sockets;Socket;false;Accept;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -4185,7 +4087,6 @@ | System.Net.Sockets;Socket;false;ConnectAsync;(System.Net.Sockets.SocketAsyncEventArgs);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Sockets;Socket;false;ConnectAsync;(System.Net.Sockets.SocketAsyncEventArgs);;Argument[Qualifier];Argument[0];taint;generated | | System.Net.Sockets;Socket;false;DisconnectAsync;(System.Net.Sockets.SocketAsyncEventArgs);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Sockets;Socket;false;EndAccept;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | | System.Net.Sockets;Socket;false;ReceiveAsync;(System.Net.Sockets.SocketAsyncEventArgs);;Argument[Qualifier];Argument[0];taint;generated | | System.Net.Sockets;Socket;false;ReceiveFrom;(System.Byte[],System.Int32,System.Int32,System.Net.Sockets.SocketFlags,System.Net.EndPoint);;Argument[4];Argument[Qualifier];taint;generated | | System.Net.Sockets;Socket;false;ReceiveFrom;(System.Byte[],System.Int32,System.Int32,System.Net.Sockets.SocketFlags,System.Net.EndPoint);;Argument[4];ReturnValue;taint;generated | @@ -4228,7 +4129,6 @@ | System.Net.Sockets;SocketAsyncEventArgs;false;set_SendPacketsElements;(System.Net.Sockets.SendPacketsElement[]);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.Net.Sockets;SocketAsyncEventArgs;false;set_UserToken;(System.Object);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Sockets;SocketException;false;get_Message;();;Argument[Qualifier];ReturnValue;taint;generated | -| System.Net.Sockets;SocketTaskExtensions;false;AcceptAsync;(System.Net.Sockets.Socket,System.Net.Sockets.Socket);;Argument[1];ReturnValue;taint;generated | | System.Net.Sockets;SocketTaskExtensions;false;ConnectAsync;(System.Net.Sockets.Socket,System.Net.EndPoint);;Argument[1];Argument[0];taint;generated | | System.Net.Sockets;SocketTaskExtensions;false;ConnectAsync;(System.Net.Sockets.Socket,System.Net.EndPoint,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Net.Sockets;SocketTaskExtensions;false;ConnectAsync;(System.Net.Sockets.Socket,System.Net.EndPoint,System.Threading.CancellationToken);;Argument[1];Argument[0];taint;generated | @@ -4240,8 +4140,6 @@ | System.Net.Sockets;SocketTaskExtensions;false;ReceiveAsync;(System.Net.Sockets.Socket,System.Memory,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Net.Sockets;SocketTaskExtensions;false;ReceiveAsync;(System.Net.Sockets.Socket,System.Memory,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | | System.Net.Sockets;SocketTaskExtensions;false;ReceiveAsync;(System.Net.Sockets.Socket,System.Memory,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken);;Argument[3];ReturnValue;taint;generated | -| System.Net.Sockets;SocketTaskExtensions;false;ReceiveFromAsync;(System.Net.Sockets.Socket,System.ArraySegment,System.Net.Sockets.SocketFlags,System.Net.EndPoint);;Argument[3];ReturnValue;taint;generated | -| System.Net.Sockets;SocketTaskExtensions;false;ReceiveMessageFromAsync;(System.Net.Sockets.Socket,System.ArraySegment,System.Net.Sockets.SocketFlags,System.Net.EndPoint);;Argument[3];ReturnValue;taint;generated | | System.Net.Sockets;SocketTaskExtensions;false;SendAsync;(System.Net.Sockets.Socket,System.ReadOnlyMemory,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Net.Sockets;SocketTaskExtensions;false;SendAsync;(System.Net.Sockets.Socket,System.ReadOnlyMemory,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken);;Argument[3];ReturnValue;taint;generated | | System.Net.Sockets;SocketTaskExtensions;false;SendToAsync;(System.Net.Sockets.Socket,System.ArraySegment,System.Net.Sockets.SocketFlags,System.Net.EndPoint);;Argument[3];Argument[0];taint;generated | @@ -4252,8 +4150,6 @@ | System.Net.Sockets;TcpClient;false;set_Client;(System.Net.Sockets.Socket);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Sockets;TcpListener;false;AcceptSocket;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Sockets;TcpListener;false;AcceptTcpClient;();;Argument[Qualifier];ReturnValue;taint;generated | -| System.Net.Sockets;TcpListener;false;EndAcceptSocket;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | -| System.Net.Sockets;TcpListener;false;EndAcceptTcpClient;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | | System.Net.Sockets;TcpListener;false;TcpListener;(System.Net.IPAddress,System.Int32);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Sockets;TcpListener;false;TcpListener;(System.Net.IPEndPoint);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Sockets;TcpListener;false;get_LocalEndpoint;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -4325,17 +4221,11 @@ | System.Net;CookieCollection;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated | | System.Net;CookieException;false;GetObjectData;(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext);;Argument[Qualifier];Argument[0];taint;generated | | System.Net;CredentialCache;false;GetCredential;(System.Uri,System.String);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Net;Dns;false;EndGetHostAddresses;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | -| System.Net;Dns;false;EndGetHostByName;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | -| System.Net;Dns;false;EndGetHostEntry;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | -| System.Net;Dns;false;EndResolve;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | | System.Net;DnsEndPoint;false;DnsEndPoint;(System.String,System.Int32,System.Net.Sockets.AddressFamily);;Argument[0];Argument[Qualifier];taint;generated | | System.Net;DnsEndPoint;false;ToString;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net;DnsEndPoint;false;get_Host;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net;DownloadDataCompletedEventArgs;false;get_Result;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net;DownloadStringCompletedEventArgs;false;get_Result;();;Argument[Qualifier];ReturnValue;taint;generated | -| System.Net;FileWebRequest;false;EndGetRequestStream;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | -| System.Net;FileWebRequest;false;EndGetResponse;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | | System.Net;FileWebRequest;false;GetRequestStream;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net;FileWebRequest;false;GetResponse;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net;FileWebRequest;false;get_ContentType;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -4410,9 +4300,6 @@ | System.Net;HttpListenerTimeoutManager;false;get_IdleConnection;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net;HttpListenerTimeoutManager;false;set_DrainEntityBody;(System.TimeSpan);;Argument[0];Argument[Qualifier];taint;generated | | System.Net;HttpListenerTimeoutManager;false;set_IdleConnection;(System.TimeSpan);;Argument[0];Argument[Qualifier];taint;generated | -| System.Net;HttpWebRequest;false;EndGetRequestStream;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | -| System.Net;HttpWebRequest;false;EndGetRequestStream;(System.IAsyncResult,System.Net.TransportContext);;Argument[0];ReturnValue;taint;generated | -| System.Net;HttpWebRequest;false;EndGetResponse;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | | System.Net;HttpWebRequest;false;GetRequestStream;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net;HttpWebRequest;false;GetRequestStream;(System.Net.TransportContext);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net;HttpWebRequest;false;GetResponse;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -4498,8 +4385,6 @@ | System.Net;WebClient;false;GetWebResponse;(System.Net.WebRequest);;Argument[0];ReturnValue;taint;generated | | System.Net;WebClient;false;GetWebResponse;(System.Net.WebRequest,System.IAsyncResult);;Argument[0];Argument[Qualifier];taint;generated | | System.Net;WebClient;false;GetWebResponse;(System.Net.WebRequest,System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | -| System.Net;WebClient;false;GetWebResponse;(System.Net.WebRequest,System.IAsyncResult);;Argument[1];Argument[Qualifier];taint;generated | -| System.Net;WebClient;false;GetWebResponse;(System.Net.WebRequest,System.IAsyncResult);;Argument[1];ReturnValue;taint;generated | | System.Net;WebClient;false;OpenRead;(System.String);;Argument[0];Argument[Qualifier];taint;generated | | System.Net;WebClient;false;OpenRead;(System.String);;Argument[0];ReturnValue;taint;generated | | System.Net;WebClient;false;OpenRead;(System.String);;Argument[Qualifier];ReturnValue;taint;generated | @@ -5368,7 +5253,6 @@ | System.Resources;ResourceReader;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Resources;ResourceReader;false;GetResourceData;(System.String,System.String,System.Byte[]);;Argument[Qualifier];ReturnValue;taint;generated | | System.Resources;ResourceReader;false;ResourceReader;(System.IO.Stream);;Argument[0];Argument[Qualifier];taint;generated | -| System.Resources;ResourceSet;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Resources;ResourceSet;false;ResourceSet;(System.IO.Stream);;Argument[0];Argument[Qualifier];taint;generated | | System.Resources;ResourceSet;false;ResourceSet;(System.Resources.IResourceReader);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.Resources;ResourceWriter;false;ResourceWriter;(System.IO.Stream);;Argument[0];Argument[Qualifier];taint;generated | @@ -5733,7 +5617,6 @@ | System.Security.Cryptography.X509Certificates;X509Certificate;false;ToString;(System.Boolean);;Argument[Qualifier];ReturnValue;taint;generated | | System.Security.Cryptography.X509Certificates;X509Certificate;false;get_Issuer;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Security.Cryptography.X509Certificates;X509Certificate;false;get_Subject;();;Argument[Qualifier];ReturnValue;taint;generated | -| System.Security.Cryptography.X509Certificates;X509CertificateCollection+X509CertificateEnumerator;false;X509CertificateEnumerator;(System.Security.Cryptography.X509Certificates.X509CertificateCollection);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.Security.Cryptography.X509Certificates;X509CertificateCollection+X509CertificateEnumerator;false;get_Current;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Security.Cryptography.X509Certificates;X509CertificateCollection;false;Add;(System.Security.Cryptography.X509Certificates.X509Certificate);;Argument[0];Argument[Qualifier].Element;value;manual | | System.Security.Cryptography.X509Certificates;X509CertificateCollection;false;AddRange;(System.Security.Cryptography.X509Certificates.X509CertificateCollection);;Argument[0].Element;Argument[Qualifier].Element;value;manual | @@ -6224,10 +6107,6 @@ | System.Threading.Tasks.Dataflow;DataflowBlock;false;Receive<>;(System.Threading.Tasks.Dataflow.ISourceBlock,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Threading.Tasks.Dataflow;DataflowBlock;false;Receive<>;(System.Threading.Tasks.Dataflow.ISourceBlock,System.TimeSpan);;Argument[0];ReturnValue;taint;generated | | System.Threading.Tasks.Dataflow;DataflowBlock;false;Receive<>;(System.Threading.Tasks.Dataflow.ISourceBlock,System.TimeSpan,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | -| System.Threading.Tasks.Dataflow;DataflowBlock;false;ReceiveAsync<>;(System.Threading.Tasks.Dataflow.ISourceBlock);;Argument[0];ReturnValue;taint;generated | -| System.Threading.Tasks.Dataflow;DataflowBlock;false;ReceiveAsync<>;(System.Threading.Tasks.Dataflow.ISourceBlock,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | -| System.Threading.Tasks.Dataflow;DataflowBlock;false;ReceiveAsync<>;(System.Threading.Tasks.Dataflow.ISourceBlock,System.TimeSpan);;Argument[0];ReturnValue;taint;generated | -| System.Threading.Tasks.Dataflow;DataflowBlock;false;ReceiveAsync<>;(System.Threading.Tasks.Dataflow.ISourceBlock,System.TimeSpan,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Threading.Tasks.Dataflow;DataflowBlock;false;SendAsync<>;(System.Threading.Tasks.Dataflow.ITargetBlock,TInput,System.Threading.CancellationToken);;Argument[1];Argument[0];taint;generated | | System.Threading.Tasks.Dataflow;DataflowBlock;false;TryReceive<>;(System.Threading.Tasks.Dataflow.IReceivableSourceBlock,TOutput);;Argument[0];ReturnValue;taint;generated | | System.Threading.Tasks.Dataflow;DataflowBlockOptions;false;get_CancellationToken;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -7010,12 +6889,9 @@ | System.Xml.Schema;XmlSchemaSet;false;Add;(System.String,System.Xml.XmlReader);;Argument[Qualifier];ReturnValue;taint;generated | | System.Xml.Schema;XmlSchemaSet;false;Add;(System.Xml.Schema.XmlSchema);;Argument[0];Argument[Qualifier];taint;generated | | System.Xml.Schema;XmlSchemaSet;false;Add;(System.Xml.Schema.XmlSchema);;Argument[0];ReturnValue;taint;generated | -| System.Xml.Schema;XmlSchemaSet;false;Add;(System.Xml.Schema.XmlSchemaSet);;Argument[0];Argument[Qualifier];taint;generated | -| System.Xml.Schema;XmlSchemaSet;false;CopyTo;(System.Xml.Schema.XmlSchema[],System.Int32);;Argument[Qualifier];Argument[0].Element;taint;generated | | System.Xml.Schema;XmlSchemaSet;false;Remove;(System.Xml.Schema.XmlSchema);;Argument[0];ReturnValue;taint;generated | | System.Xml.Schema;XmlSchemaSet;false;Reprocess;(System.Xml.Schema.XmlSchema);;Argument[0];Argument[Qualifier];taint;generated | | System.Xml.Schema;XmlSchemaSet;false;Reprocess;(System.Xml.Schema.XmlSchema);;Argument[0];ReturnValue;taint;generated | -| System.Xml.Schema;XmlSchemaSet;false;Schemas;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Xml.Schema;XmlSchemaSet;false;XmlSchemaSet;(System.Xml.XmlNameTable);;Argument[0];Argument[Qualifier];taint;generated | | System.Xml.Schema;XmlSchemaSet;false;get_CompilationSettings;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Xml.Schema;XmlSchemaSet;false;get_GlobalAttributes;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -7102,10 +6978,8 @@ | System.Xml.Schema;XmlSchemaXPath;false;get_XPath;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Xml.Schema;XmlSchemaXPath;false;set_XPath;(System.String);;Argument[0];Argument[Qualifier];taint;generated | | System.Xml.Serialization;CodeIdentifiers;false;Add;(System.String,System.Object);;Argument[1];Argument[Qualifier];taint;generated | -| System.Xml.Serialization;CodeIdentifiers;false;Add;(System.String,System.Object);;Argument[Qualifier];Argument[1];taint;generated | | System.Xml.Serialization;CodeIdentifiers;false;AddUnique;(System.String,System.Object);;Argument[0];ReturnValue;taint;generated | | System.Xml.Serialization;CodeIdentifiers;false;AddUnique;(System.String,System.Object);;Argument[1];Argument[Qualifier];taint;generated | -| System.Xml.Serialization;CodeIdentifiers;false;AddUnique;(System.String,System.Object);;Argument[Qualifier];Argument[1];taint;generated | | System.Xml.Serialization;CodeIdentifiers;false;MakeUnique;(System.String);;Argument[0];ReturnValue;taint;generated | | System.Xml.Serialization;CodeIdentifiers;false;ToArray;(System.Type);;Argument[Qualifier];ReturnValue;taint;generated | | System.Xml.Serialization;ImportContext;false;ImportContext;(System.Xml.Serialization.CodeIdentifiers,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | @@ -7411,7 +7285,6 @@ | System.Xml.Serialization;XmlSerializationWriter;false;WriteElementStringRaw;(System.String,System.String,System.String);;Argument[2];Argument[Qualifier];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WriteElementStringRaw;(System.String,System.String,System.String,System.Xml.XmlQualifiedName);;Argument[2];Argument[Qualifier];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WriteElementStringRaw;(System.String,System.String,System.Xml.XmlQualifiedName);;Argument[1];Argument[Qualifier];taint;generated | -| System.Xml.Serialization;XmlSerializationWriter;false;WriteId;(System.Object);;Argument[Qualifier];Argument[0];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WriteNullableStringEncoded;(System.String,System.String,System.String,System.Xml.XmlQualifiedName);;Argument[2];Argument[Qualifier];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WriteNullableStringEncodedRaw;(System.String,System.String,System.Byte[],System.Xml.XmlQualifiedName);;Argument[2].Element;Argument[Qualifier];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WriteNullableStringEncodedRaw;(System.String,System.String,System.String,System.Xml.XmlQualifiedName);;Argument[2];Argument[Qualifier];taint;generated | @@ -7419,15 +7292,9 @@ | System.Xml.Serialization;XmlSerializationWriter;false;WriteNullableStringLiteralRaw;(System.String,System.String,System.Byte[]);;Argument[2].Element;Argument[Qualifier];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WriteNullableStringLiteralRaw;(System.String,System.String,System.String);;Argument[2];Argument[Qualifier];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object);;Argument[2];Argument[Qualifier];taint;generated | -| System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object);;Argument[Qualifier];Argument[2];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object,System.Type);;Argument[2];Argument[Qualifier];taint;generated | -| System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object,System.Type);;Argument[Qualifier];Argument[2];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object,System.Type,System.Boolean);;Argument[2];Argument[Qualifier];taint;generated | -| System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object,System.Type,System.Boolean);;Argument[Qualifier];Argument[2];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object,System.Type,System.Boolean,System.Boolean);;Argument[2];Argument[Qualifier];taint;generated | -| System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object,System.Type,System.Boolean,System.Boolean);;Argument[Qualifier];Argument[2];taint;generated | -| System.Xml.Serialization;XmlSerializationWriter;false;WriteReferencingElement;(System.String,System.String,System.Object);;Argument[Qualifier];Argument[2];taint;generated | -| System.Xml.Serialization;XmlSerializationWriter;false;WriteReferencingElement;(System.String,System.String,System.Object,System.Boolean);;Argument[Qualifier];Argument[2];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WriteRpcResult;(System.String,System.String);;Argument[0];Argument[Qualifier];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WriteRpcResult;(System.String,System.String);;Argument[1];Argument[Qualifier];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WriteSerializable;(System.Xml.Serialization.IXmlSerializable,System.String,System.String,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | @@ -9087,27 +8954,6 @@ | System;TupleExtensions;false;Deconstruct<,>;(System.Tuple,T1,T2);;Argument[0].Property[System.Tuple<,>.Item1];Argument[1];value;manual | | System;TupleExtensions;false;Deconstruct<,>;(System.Tuple,T1,T2);;Argument[0].Property[System.Tuple<,>.Item2];Argument[2];value;manual | | System;TupleExtensions;false;Deconstruct<>;(System.Tuple,T1);;Argument[0].Property[System.Tuple<>.Item1];Argument[1];value;manual | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated | | System;TupleExtensions;false;ToValueTuple<,,,,,,,,,,,,,,,,,,,,>;(System.Tuple>>);;Argument[0];ReturnValue;taint;generated | | System;TupleExtensions;false;ToValueTuple<,,,,,,,,,,,,,,,,,,,>;(System.Tuple>>);;Argument[0];ReturnValue;taint;generated | | System;TupleExtensions;false;ToValueTuple<,,,,,,,,,,,,,,,,,,>;(System.Tuple>>);;Argument[0];ReturnValue;taint;generated | From 813a8548d7271144696e9838d4b5aa1f72468253 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Wed, 22 Jun 2022 16:42:42 -0400 Subject: [PATCH 080/505] C++: accept test changes for globals in data flow --- .../semmle/tests/ExposedSystemData.expected | 12 ++++++ .../PotentiallyExposedSystemData.expected | 17 ++++++++ .../CWE/CWE-497/semmle/tests/tests.cpp | 4 +- .../CWE/CWE-497/semmle/tests/tests2.cpp | 2 +- .../Security/CWE/CWE-611/XXE.expected | 40 +++++++++++++++++++ .../Security/CWE/CWE-611/tests3.cpp | 6 +-- .../Security/CWE/CWE-611/tests5.cpp | 4 +- 7 files changed, 77 insertions(+), 8 deletions(-) diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/ExposedSystemData.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/ExposedSystemData.expected index 80b195bd0bd..11ec8e849a5 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/ExposedSystemData.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/ExposedSystemData.expected @@ -1,4 +1,8 @@ edges +| tests2.cpp:50:13:50:19 | global1 | tests2.cpp:82:14:82:20 | global1 | +| tests2.cpp:50:13:50:19 | global1 | tests2.cpp:82:14:82:20 | global1 | +| tests2.cpp:50:23:50:43 | Store | tests2.cpp:50:13:50:19 | global1 | +| tests2.cpp:50:23:50:43 | call to mysql_get_client_info | tests2.cpp:50:23:50:43 | Store | | tests2.cpp:63:13:63:18 | call to getenv | tests2.cpp:63:13:63:26 | (const char *)... | | tests2.cpp:64:13:64:18 | call to getenv | tests2.cpp:64:13:64:26 | (const char *)... | | tests2.cpp:65:13:65:18 | call to getenv | tests2.cpp:65:13:65:30 | (const char *)... | @@ -6,6 +10,8 @@ edges | tests2.cpp:78:18:78:38 | call to mysql_get_client_info | tests2.cpp:81:14:81:19 | (const char *)... | | tests2.cpp:80:14:80:34 | call to mysql_get_client_info | tests2.cpp:80:14:80:34 | call to mysql_get_client_info | | tests2.cpp:80:14:80:34 | call to mysql_get_client_info | tests2.cpp:80:14:80:34 | call to mysql_get_client_info | +| tests2.cpp:82:14:82:20 | global1 | tests2.cpp:82:14:82:20 | global1 | +| tests2.cpp:82:14:82:20 | global1 | tests2.cpp:82:14:82:20 | global1 | | tests2.cpp:91:42:91:45 | str1 | tests2.cpp:93:14:93:17 | str1 | | tests2.cpp:101:8:101:15 | call to getpwuid | tests2.cpp:102:14:102:15 | pw | | tests2.cpp:109:3:109:4 | c1 [post update] [ptr] | tests2.cpp:111:14:111:15 | c1 [read] [ptr] | @@ -23,6 +29,9 @@ edges | tests_sysconf.cpp:36:21:36:27 | confstr output argument | tests_sysconf.cpp:39:19:39:25 | (const void *)... | | tests_sysconf.cpp:36:21:36:27 | confstr output argument | tests_sysconf.cpp:39:19:39:25 | pathbuf | nodes +| tests2.cpp:50:13:50:19 | global1 | semmle.label | global1 | +| tests2.cpp:50:23:50:43 | Store | semmle.label | Store | +| tests2.cpp:50:23:50:43 | call to mysql_get_client_info | semmle.label | call to mysql_get_client_info | | tests2.cpp:63:13:63:18 | call to getenv | semmle.label | call to getenv | | tests2.cpp:63:13:63:18 | call to getenv | semmle.label | call to getenv | | tests2.cpp:63:13:63:26 | (const char *)... | semmle.label | (const char *)... | @@ -39,6 +48,8 @@ nodes | tests2.cpp:80:14:80:34 | call to mysql_get_client_info | semmle.label | call to mysql_get_client_info | | tests2.cpp:80:14:80:34 | call to mysql_get_client_info | semmle.label | call to mysql_get_client_info | | tests2.cpp:81:14:81:19 | (const char *)... | semmle.label | (const char *)... | +| tests2.cpp:82:14:82:20 | global1 | semmle.label | global1 | +| tests2.cpp:82:14:82:20 | global1 | semmle.label | global1 | | tests2.cpp:91:42:91:45 | str1 | semmle.label | str1 | | tests2.cpp:93:14:93:17 | str1 | semmle.label | str1 | | tests2.cpp:101:8:101:15 | call to getpwuid | semmle.label | call to getpwuid | @@ -70,6 +81,7 @@ subpaths | tests2.cpp:80:14:80:34 | call to mysql_get_client_info | tests2.cpp:80:14:80:34 | call to mysql_get_client_info | tests2.cpp:80:14:80:34 | call to mysql_get_client_info | This operation exposes system data from $@. | tests2.cpp:80:14:80:34 | call to mysql_get_client_info | call to mysql_get_client_info | | tests2.cpp:80:14:80:34 | call to mysql_get_client_info | tests2.cpp:80:14:80:34 | call to mysql_get_client_info | tests2.cpp:80:14:80:34 | call to mysql_get_client_info | This operation exposes system data from $@. | tests2.cpp:80:14:80:34 | call to mysql_get_client_info | call to mysql_get_client_info | | tests2.cpp:81:14:81:19 | (const char *)... | tests2.cpp:78:18:78:38 | call to mysql_get_client_info | tests2.cpp:81:14:81:19 | (const char *)... | This operation exposes system data from $@. | tests2.cpp:78:18:78:38 | call to mysql_get_client_info | call to mysql_get_client_info | +| tests2.cpp:82:14:82:20 | global1 | tests2.cpp:50:23:50:43 | call to mysql_get_client_info | tests2.cpp:82:14:82:20 | global1 | This operation exposes system data from $@. | tests2.cpp:50:23:50:43 | call to mysql_get_client_info | call to mysql_get_client_info | | tests2.cpp:93:14:93:17 | str1 | tests2.cpp:91:42:91:45 | str1 | tests2.cpp:93:14:93:17 | str1 | This operation exposes system data from $@. | tests2.cpp:91:42:91:45 | str1 | str1 | | tests2.cpp:102:14:102:15 | pw | tests2.cpp:101:8:101:15 | call to getpwuid | tests2.cpp:102:14:102:15 | pw | This operation exposes system data from $@. | tests2.cpp:101:8:101:15 | call to getpwuid | call to getpwuid | | tests2.cpp:111:14:111:19 | (const char *)... | tests2.cpp:109:12:109:17 | call to getenv | tests2.cpp:111:14:111:19 | (const char *)... | This operation exposes system data from $@. | tests2.cpp:109:12:109:17 | call to getenv | call to getenv | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/PotentiallyExposedSystemData.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/PotentiallyExposedSystemData.expected index 62fe44dcc23..ff10a3b9c1c 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/PotentiallyExposedSystemData.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/PotentiallyExposedSystemData.expected @@ -5,6 +5,14 @@ edges | tests.cpp:57:18:57:23 | call to getenv | tests.cpp:57:18:57:39 | (const char_type *)... | | tests.cpp:58:41:58:46 | call to getenv | tests.cpp:58:41:58:62 | (const char_type *)... | | tests.cpp:59:43:59:48 | call to getenv | tests.cpp:59:43:59:64 | (const char *)... | +| tests.cpp:62:7:62:18 | global_token | tests.cpp:69:17:69:28 | global_token | +| tests.cpp:62:7:62:18 | global_token | tests.cpp:71:27:71:38 | global_token | +| tests.cpp:62:7:62:18 | global_token | tests.cpp:71:27:71:38 | global_token | +| tests.cpp:62:22:62:27 | Store | tests.cpp:62:7:62:18 | global_token | +| tests.cpp:62:22:62:27 | call to getenv | tests.cpp:62:22:62:27 | Store | +| tests.cpp:69:17:69:28 | global_token | tests.cpp:73:27:73:31 | maybe | +| tests.cpp:71:27:71:38 | global_token | tests.cpp:71:27:71:38 | global_token | +| tests.cpp:71:27:71:38 | global_token | tests.cpp:71:27:71:38 | global_token | | tests.cpp:86:29:86:31 | *msg | tests.cpp:88:15:88:17 | msg | | tests.cpp:86:29:86:31 | msg | tests.cpp:88:15:88:17 | msg | | tests.cpp:97:13:97:18 | call to getenv | tests.cpp:97:13:97:34 | (const char *)... | @@ -52,6 +60,13 @@ nodes | tests.cpp:59:43:59:48 | call to getenv | semmle.label | call to getenv | | tests.cpp:59:43:59:48 | call to getenv | semmle.label | call to getenv | | tests.cpp:59:43:59:64 | (const char *)... | semmle.label | (const char *)... | +| tests.cpp:62:7:62:18 | global_token | semmle.label | global_token | +| tests.cpp:62:22:62:27 | Store | semmle.label | Store | +| tests.cpp:62:22:62:27 | call to getenv | semmle.label | call to getenv | +| tests.cpp:69:17:69:28 | global_token | semmle.label | global_token | +| tests.cpp:71:27:71:38 | global_token | semmle.label | global_token | +| tests.cpp:71:27:71:38 | global_token | semmle.label | global_token | +| tests.cpp:73:27:73:31 | maybe | semmle.label | maybe | | tests.cpp:86:29:86:31 | *msg | semmle.label | *msg | | tests.cpp:86:29:86:31 | msg | semmle.label | msg | | tests.cpp:88:15:88:17 | msg | semmle.label | msg | @@ -97,6 +112,8 @@ subpaths | tests.cpp:58:41:58:62 | (const char_type *)... | tests.cpp:58:41:58:46 | call to getenv | tests.cpp:58:41:58:62 | (const char_type *)... | This operation potentially exposes sensitive system data from $@. | tests.cpp:58:41:58:46 | call to getenv | call to getenv | | tests.cpp:59:43:59:48 | call to getenv | tests.cpp:59:43:59:48 | call to getenv | tests.cpp:59:43:59:48 | call to getenv | This operation potentially exposes sensitive system data from $@. | tests.cpp:59:43:59:48 | call to getenv | call to getenv | | tests.cpp:59:43:59:64 | (const char *)... | tests.cpp:59:43:59:48 | call to getenv | tests.cpp:59:43:59:64 | (const char *)... | This operation potentially exposes sensitive system data from $@. | tests.cpp:59:43:59:48 | call to getenv | call to getenv | +| tests.cpp:71:27:71:38 | global_token | tests.cpp:62:22:62:27 | call to getenv | tests.cpp:71:27:71:38 | global_token | This operation potentially exposes sensitive system data from $@. | tests.cpp:62:22:62:27 | call to getenv | call to getenv | +| tests.cpp:73:27:73:31 | maybe | tests.cpp:62:22:62:27 | call to getenv | tests.cpp:73:27:73:31 | maybe | This operation potentially exposes sensitive system data from $@. | tests.cpp:62:22:62:27 | call to getenv | call to getenv | | tests.cpp:88:15:88:17 | msg | tests.cpp:97:13:97:18 | call to getenv | tests.cpp:88:15:88:17 | msg | This operation potentially exposes sensitive system data from $@. | tests.cpp:97:13:97:18 | call to getenv | call to getenv | | tests.cpp:97:13:97:18 | call to getenv | tests.cpp:97:13:97:18 | call to getenv | tests.cpp:97:13:97:18 | call to getenv | This operation potentially exposes sensitive system data from $@. | tests.cpp:97:13:97:18 | call to getenv | call to getenv | | tests.cpp:97:13:97:34 | (const char *)... | tests.cpp:97:13:97:18 | call to getenv | tests.cpp:97:13:97:34 | (const char *)... | This operation potentially exposes sensitive system data from $@. | tests.cpp:97:13:97:18 | call to getenv | call to getenv | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/tests.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/tests.cpp index e61fc582fc3..843d579386b 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/tests.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/tests.cpp @@ -68,9 +68,9 @@ void test2(bool cond) maybe = cond ? global_token : global_other; - printf("token = '%s'\n", global_token); // BAD: outputs SECRET_TOKEN environment variable [NOT DETECTED] + printf("token = '%s'\n", global_token); // BAD: outputs SECRET_TOKEN environment variable printf("other = '%s'\n", global_other); - printf("maybe = '%s'\n", maybe); // BAD: may output SECRET_TOKEN environment variable [NOT DETECTED] + printf("maybe = '%s'\n", maybe); // BAD: may output SECRET_TOKEN environment variable } void test3() diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/tests2.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/tests2.cpp index 763f1ecfafc..e1399eab343 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/tests2.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/tests2.cpp @@ -79,7 +79,7 @@ void test1() send(sock, mysql_get_client_info(), val(), val()); // BAD send(sock, buffer, val(), val()); // BAD - send(sock, global1, val(), val()); // BAD [NOT DETECTED] + send(sock, global1, val(), val()); // BAD send(sock, global2, val(), val()); // GOOD: not system data } diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-611/XXE.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-611/XXE.expected index a4db6155e31..7403cba893a 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-611/XXE.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-611/XXE.expected @@ -2,11 +2,26 @@ edges | tests2.cpp:20:17:20:31 | SAXParser output argument | tests2.cpp:22:2:22:2 | p | | tests2.cpp:33:17:33:31 | SAXParser output argument | tests2.cpp:37:2:37:2 | p | | tests3.cpp:23:21:23:53 | call to createXMLReader | tests3.cpp:25:2:25:2 | p | +| tests3.cpp:35:16:35:20 | p_3_3 | tests3.cpp:38:2:38:6 | p_3_3 | +| tests3.cpp:35:24:35:56 | Store | tests3.cpp:35:16:35:20 | p_3_3 | +| tests3.cpp:35:24:35:56 | call to createXMLReader | tests3.cpp:35:24:35:56 | Store | +| tests3.cpp:41:16:41:20 | p_3_4 | tests3.cpp:45:2:45:6 | p_3_4 | +| tests3.cpp:41:24:41:56 | Store | tests3.cpp:41:16:41:20 | p_3_4 | +| tests3.cpp:41:24:41:56 | call to createXMLReader | tests3.cpp:41:24:41:56 | Store | +| tests3.cpp:48:16:48:20 | p_3_5 | tests3.cpp:56:2:56:6 | p_3_5 | +| tests3.cpp:48:24:48:56 | Store | tests3.cpp:48:16:48:20 | p_3_5 | +| tests3.cpp:48:24:48:56 | call to createXMLReader | tests3.cpp:48:24:48:56 | Store | | tests3.cpp:60:21:60:53 | call to createXMLReader | tests3.cpp:63:2:63:2 | p | | tests3.cpp:67:21:67:53 | call to createXMLReader | tests3.cpp:70:2:70:2 | p | | tests5.cpp:27:25:27:38 | call to createLSParser | tests5.cpp:29:2:29:2 | p | | tests5.cpp:40:25:40:38 | call to createLSParser | tests5.cpp:43:2:43:2 | p | | tests5.cpp:55:25:55:38 | call to createLSParser | tests5.cpp:59:2:59:2 | p | +| tests5.cpp:63:14:63:17 | g_p1 | tests5.cpp:76:2:76:5 | g_p1 | +| tests5.cpp:63:21:63:24 | g_p2 | tests5.cpp:77:2:77:5 | g_p2 | +| tests5.cpp:67:2:67:32 | Store | tests5.cpp:63:14:63:17 | g_p1 | +| tests5.cpp:67:17:67:30 | call to createLSParser | tests5.cpp:67:2:67:32 | Store | +| tests5.cpp:70:2:70:32 | Store | tests5.cpp:63:21:63:24 | g_p2 | +| tests5.cpp:70:17:70:30 | call to createLSParser | tests5.cpp:70:2:70:32 | Store | | tests5.cpp:81:25:81:38 | call to createLSParser | tests5.cpp:83:2:83:2 | p | | tests5.cpp:81:25:81:38 | call to createLSParser | tests5.cpp:83:2:83:2 | p | | tests5.cpp:83:2:83:2 | p | tests5.cpp:85:2:85:2 | p | @@ -46,6 +61,18 @@ nodes | tests2.cpp:37:2:37:2 | p | semmle.label | p | | tests3.cpp:23:21:23:53 | call to createXMLReader | semmle.label | call to createXMLReader | | tests3.cpp:25:2:25:2 | p | semmle.label | p | +| tests3.cpp:35:16:35:20 | p_3_3 | semmle.label | p_3_3 | +| tests3.cpp:35:24:35:56 | Store | semmle.label | Store | +| tests3.cpp:35:24:35:56 | call to createXMLReader | semmle.label | call to createXMLReader | +| tests3.cpp:38:2:38:6 | p_3_3 | semmle.label | p_3_3 | +| tests3.cpp:41:16:41:20 | p_3_4 | semmle.label | p_3_4 | +| tests3.cpp:41:24:41:56 | Store | semmle.label | Store | +| tests3.cpp:41:24:41:56 | call to createXMLReader | semmle.label | call to createXMLReader | +| tests3.cpp:45:2:45:6 | p_3_4 | semmle.label | p_3_4 | +| tests3.cpp:48:16:48:20 | p_3_5 | semmle.label | p_3_5 | +| tests3.cpp:48:24:48:56 | Store | semmle.label | Store | +| tests3.cpp:48:24:48:56 | call to createXMLReader | semmle.label | call to createXMLReader | +| tests3.cpp:56:2:56:6 | p_3_5 | semmle.label | p_3_5 | | tests3.cpp:60:21:60:53 | call to createXMLReader | semmle.label | call to createXMLReader | | tests3.cpp:63:2:63:2 | p | semmle.label | p | | tests3.cpp:67:21:67:53 | call to createXMLReader | semmle.label | call to createXMLReader | @@ -61,6 +88,14 @@ nodes | tests5.cpp:43:2:43:2 | p | semmle.label | p | | tests5.cpp:55:25:55:38 | call to createLSParser | semmle.label | call to createLSParser | | tests5.cpp:59:2:59:2 | p | semmle.label | p | +| tests5.cpp:63:14:63:17 | g_p1 | semmle.label | g_p1 | +| tests5.cpp:63:21:63:24 | g_p2 | semmle.label | g_p2 | +| tests5.cpp:67:2:67:32 | Store | semmle.label | Store | +| tests5.cpp:67:17:67:30 | call to createLSParser | semmle.label | call to createLSParser | +| tests5.cpp:70:2:70:32 | Store | semmle.label | Store | +| tests5.cpp:70:17:70:30 | call to createLSParser | semmle.label | call to createLSParser | +| tests5.cpp:76:2:76:5 | g_p1 | semmle.label | g_p1 | +| tests5.cpp:77:2:77:5 | g_p2 | semmle.label | g_p2 | | tests5.cpp:81:25:81:38 | call to createLSParser | semmle.label | call to createLSParser | | tests5.cpp:83:2:83:2 | p | semmle.label | p | | tests5.cpp:83:2:83:2 | p | semmle.label | p | @@ -108,6 +143,9 @@ subpaths | tests2.cpp:22:2:22:2 | p | tests2.cpp:20:17:20:31 | SAXParser output argument | tests2.cpp:22:2:22:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests2.cpp:20:17:20:31 | SAXParser output argument | XML parser | | tests2.cpp:37:2:37:2 | p | tests2.cpp:33:17:33:31 | SAXParser output argument | tests2.cpp:37:2:37:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests2.cpp:33:17:33:31 | SAXParser output argument | XML parser | | tests3.cpp:25:2:25:2 | p | tests3.cpp:23:21:23:53 | call to createXMLReader | tests3.cpp:25:2:25:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests3.cpp:23:21:23:53 | call to createXMLReader | XML parser | +| tests3.cpp:38:2:38:6 | p_3_3 | tests3.cpp:35:24:35:56 | call to createXMLReader | tests3.cpp:38:2:38:6 | p_3_3 | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests3.cpp:35:24:35:56 | call to createXMLReader | XML parser | +| tests3.cpp:45:2:45:6 | p_3_4 | tests3.cpp:41:24:41:56 | call to createXMLReader | tests3.cpp:45:2:45:6 | p_3_4 | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests3.cpp:41:24:41:56 | call to createXMLReader | XML parser | +| tests3.cpp:56:2:56:6 | p_3_5 | tests3.cpp:48:24:48:56 | call to createXMLReader | tests3.cpp:56:2:56:6 | p_3_5 | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests3.cpp:48:24:48:56 | call to createXMLReader | XML parser | | tests3.cpp:63:2:63:2 | p | tests3.cpp:60:21:60:53 | call to createXMLReader | tests3.cpp:63:2:63:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests3.cpp:60:21:60:53 | call to createXMLReader | XML parser | | tests3.cpp:70:2:70:2 | p | tests3.cpp:67:21:67:53 | call to createXMLReader | tests3.cpp:70:2:70:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests3.cpp:67:21:67:53 | call to createXMLReader | XML parser | | tests4.cpp:26:34:26:48 | (int)... | tests4.cpp:26:34:26:48 | (int)... | tests4.cpp:26:34:26:48 | (int)... | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests4.cpp:26:34:26:48 | (int)... | XML parser | @@ -118,6 +156,8 @@ subpaths | tests5.cpp:29:2:29:2 | p | tests5.cpp:27:25:27:38 | call to createLSParser | tests5.cpp:29:2:29:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests5.cpp:27:25:27:38 | call to createLSParser | XML parser | | tests5.cpp:43:2:43:2 | p | tests5.cpp:40:25:40:38 | call to createLSParser | tests5.cpp:43:2:43:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests5.cpp:40:25:40:38 | call to createLSParser | XML parser | | tests5.cpp:59:2:59:2 | p | tests5.cpp:55:25:55:38 | call to createLSParser | tests5.cpp:59:2:59:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests5.cpp:55:25:55:38 | call to createLSParser | XML parser | +| tests5.cpp:76:2:76:5 | g_p1 | tests5.cpp:67:17:67:30 | call to createLSParser | tests5.cpp:76:2:76:5 | g_p1 | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests5.cpp:67:17:67:30 | call to createLSParser | XML parser | +| tests5.cpp:77:2:77:5 | g_p2 | tests5.cpp:70:17:70:30 | call to createLSParser | tests5.cpp:77:2:77:5 | g_p2 | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests5.cpp:70:17:70:30 | call to createLSParser | XML parser | | tests5.cpp:83:2:83:2 | p | tests5.cpp:81:25:81:38 | call to createLSParser | tests5.cpp:83:2:83:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests5.cpp:81:25:81:38 | call to createLSParser | XML parser | | tests5.cpp:89:2:89:2 | p | tests5.cpp:81:25:81:38 | call to createLSParser | tests5.cpp:89:2:89:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests5.cpp:81:25:81:38 | call to createLSParser | XML parser | | tests.cpp:17:2:17:2 | p | tests.cpp:15:23:15:43 | XercesDOMParser output argument | tests.cpp:17:2:17:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests.cpp:15:23:15:43 | XercesDOMParser output argument | XML parser | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-611/tests3.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-611/tests3.cpp index 15e518daf13..8af560a8e6d 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-611/tests3.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-611/tests3.cpp @@ -35,14 +35,14 @@ void test3_2(InputSource &data) { SAX2XMLReader *p_3_3 = XMLReaderFactory::createXMLReader(); void test3_3(InputSource &data) { - p_3_3->parse(data); // BAD (parser not correctly configured) [NOT DETECTED] + p_3_3->parse(data); // BAD (parser not correctly configured) } SAX2XMLReader *p_3_4 = XMLReaderFactory::createXMLReader(); void test3_4(InputSource &data) { p_3_4->setFeature(XMLUni::fgXercesDisableDefaultEntityResolution, true); - p_3_4->parse(data); // GOOD + p_3_4->parse(data); // GOOD [FALSE POSITIVE] } SAX2XMLReader *p_3_5 = XMLReaderFactory::createXMLReader(); @@ -53,7 +53,7 @@ void test3_5_init() { void test3_5(InputSource &data) { test3_5_init(); - p_3_5->parse(data); // GOOD + p_3_5->parse(data); // GOOD [FALSE POSITIVE] } void test3_6(InputSource &data) { diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-611/tests5.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-611/tests5.cpp index 99027c9bd93..0d55be455da 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-611/tests5.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-611/tests5.cpp @@ -73,8 +73,8 @@ void test5_6_init() { void test5_6() { test5_6_init(); - g_p1->parse(*g_data); // GOOD - g_p2->parse(*g_data); // BAD (parser not correctly configured) [NOT DETECTED] + g_p1->parse(*g_data); // GOOD [FALSE POSITIVE] + g_p2->parse(*g_data); // BAD (parser not correctly configured) } void test5_7(DOMImplementationLS *impl, InputSource &data) { From e838b83f5f47fe5b2e086d9ee06ff09bb6213173 Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Thu, 23 Jun 2022 02:21:47 +0000 Subject: [PATCH 081/505] attempt to introduce dataflow tracking --- .../experimental/weak-params/WeakParams.ql | 69 +++++++++++++------ 1 file changed, 48 insertions(+), 21 deletions(-) diff --git a/ruby/ql/src/experimental/weak-params/WeakParams.ql b/ruby/ql/src/experimental/weak-params/WeakParams.ql index d7408f8bea7..1d8ef89e70a 100644 --- a/ruby/ql/src/experimental/weak-params/WeakParams.ql +++ b/ruby/ql/src/experimental/weak-params/WeakParams.ql @@ -1,7 +1,7 @@ /** * @name Weak or direct parameter references are used * @description Directly checking request parameters without following a strong params pattern can lead to unintentional avenues for injection attacks. - * @kind problem + * @kind path-problem * @problem.severity error * @security-severity 5.0 * @precision low @@ -10,10 +10,13 @@ */ import ruby +import codeql.ruby.DataFlow +import codeql.ruby.TaintTracking +import DataFlow::PathGraph -class WeakParams extends AstNode { +class WeakParams extends Expr { WeakParams() { - this instanceof UnspecificParamsMethod or + allParamsAccess(this) or this instanceof ParamsReference } } @@ -26,27 +29,51 @@ class StrongParamsMethod extends Method { StrongParamsMethod() { this.getName().regexpMatch(".*_params") } } -class UnspecificParamsMethod extends MethodCall { - UnspecificParamsMethod() { - ( - this.getMethodName() = "expose_all" or - this.getMethodName() = "original_hash" or - this.getMethodName() = "path_parametes" or - this.getMethodName() = "query_parameters" or - this.getMethodName() = "request_parameters" or - this.getMethodName() = "GET" or - this.getMethodName() = "POST" - ) - } +predicate allParamsAccess(MethodCall call) { + call.getMethodName() = "expose_all" or + call.getMethodName() = "original_hash" or + call.getMethodName() = "path_parametes" or + call.getMethodName() = "query_parameters" or + call.getMethodName() = "request_parameters" or + call.getMethodName() = "GET" or + call.getMethodName() = "POST" } class ParamsReference extends ElementReference { ParamsReference() { this.getAChild().toString() = "params" } } -from WeakParams params -where - not params.getEnclosingMethod() instanceof StrongParamsMethod and - params.getEnclosingModule() instanceof ControllerClass -select params, - "By exposing all keys in request parameters or by blindy accessing them, unintended parameters could be used and lead to mass-assignment or have other unexpected side-effects." +class ModelClass extends ModuleBase { + ModelClass() { + this.getModule().getSuperClass+().toString() = "ViewModel" or + this.getModule().getSuperClass+().getAnIncludedModule().toString() = "ActionModel::Model" + } +} + +class ModelClassMethodArgument extends DataFlow::Node { + private DataFlow::CallNode call; + + ModelClassMethodArgument() { + this = call.getArgument(_) and + call.getExprNode().getNode().getParent+() instanceof ModelClass + } +} + +class Configuration extends TaintTracking::Configuration { + Configuration() { this = "Configuration" } + + override predicate isSource(DataFlow::Node node) { node.asExpr().getNode() instanceof WeakParams } + + // the sink is an instance of a Model class that receives a method call + override predicate isSink(DataFlow::Node node) { node instanceof ModelClassMethodArgument } +} + +from Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink +where config.hasFlowPath(source, sink) +select sink.getNode().(ModelClassMethodArgument), source, sink, "This is bad" +// from WeakParams params +// where +// not params.getEnclosingMethod() instanceof StrongParamsMethod and +// params.getEnclosingModule() instanceof ControllerClass +// select params, +// "By exposing all keys in request parameters or by blindy accessing them, unintended parameters could be used and lead to mass-assignment or have other unexpected side-effects." From e45c982dd114aa47c13d1f63a51a4c616bfc0012 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Thu, 23 Jun 2022 14:32:52 -0400 Subject: [PATCH 082/505] C++: change note for global variables in dataflow --- cpp/ql/lib/change-notes/2022-06-23-global-var-flow.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 cpp/ql/lib/change-notes/2022-06-23-global-var-flow.md diff --git a/cpp/ql/lib/change-notes/2022-06-23-global-var-flow.md b/cpp/ql/lib/change-notes/2022-06-23-global-var-flow.md new file mode 100644 index 00000000000..695c40dbc74 --- /dev/null +++ b/cpp/ql/lib/change-notes/2022-06-23-global-var-flow.md @@ -0,0 +1,4 @@ +--- +category: minor-analysis +--- +* The IR dataflow library now includes flow through global variables. From 4a522831c4155fff716c8e37ff311d0c8d6fa4af Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Thu, 23 Jun 2022 14:39:13 -0400 Subject: [PATCH 083/505] C++: update change note for IR global var flow --- cpp/ql/lib/change-notes/2022-06-23-global-var-flow.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/ql/lib/change-notes/2022-06-23-global-var-flow.md b/cpp/ql/lib/change-notes/2022-06-23-global-var-flow.md index 695c40dbc74..ce931ef8de0 100644 --- a/cpp/ql/lib/change-notes/2022-06-23-global-var-flow.md +++ b/cpp/ql/lib/change-notes/2022-06-23-global-var-flow.md @@ -1,4 +1,4 @@ --- -category: minor-analysis +category: majorAnalysis --- -* The IR dataflow library now includes flow through global variables. +* The IR dataflow library now includes flow through global variables. This enables new findings in many scenarios. From 45dd38df6eb5da24325ebcc0456e250429e7c2b6 Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Fri, 24 Jun 2022 01:50:20 +0000 Subject: [PATCH 084/505] polish up dataflow query --- .../experimental/weak-params/WeakParams.ql | 45 +++++++++++++++---- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/ruby/ql/src/experimental/weak-params/WeakParams.ql b/ruby/ql/src/experimental/weak-params/WeakParams.ql index 1d8ef89e70a..e3f63b9bfd8 100644 --- a/ruby/ql/src/experimental/weak-params/WeakParams.ql +++ b/ruby/ql/src/experimental/weak-params/WeakParams.ql @@ -14,21 +14,38 @@ import codeql.ruby.DataFlow import codeql.ruby.TaintTracking import DataFlow::PathGraph +/** + * any direct parameters reference that happens outside of a strong params method but inside + * of a controller class + */ class WeakParams extends Expr { WeakParams() { - allParamsAccess(this) or - this instanceof ParamsReference + ( + allParamsAccess(this) or + this instanceof ParamsReference + ) and + this.getEnclosingModule() instanceof ControllerClass and + not this.getEnclosingMethod() instanceof StrongParamsMethod } } +/** + * A controller class, which extendsd `ApplicationController` + */ class ControllerClass extends ModuleBase { ControllerClass() { this.getModule().getSuperClass+().toString() = "ApplicationController" } } +/** + * A method that follows the strong params naming convention + */ class StrongParamsMethod extends Method { StrongParamsMethod() { this.getName().regexpMatch(".*_params") } } +/** + * a call to a method that exposes or accesses all parameters from an inbound HTTP request + */ predicate allParamsAccess(MethodCall call) { call.getMethodName() = "expose_all" or call.getMethodName() = "original_hash" or @@ -39,10 +56,17 @@ predicate allParamsAccess(MethodCall call) { call.getMethodName() = "POST" } +/** + * A reference to an element in the `params` object + */ class ParamsReference extends ElementReference { ParamsReference() { this.getAChild().toString() = "params" } } +/** + * returns either Model or ViewModel classes with a base class of `ViewModel` or includes `ActionModel::Model`, + * which are required to support the strong parameters pattern + */ class ModelClass extends ModuleBase { ModelClass() { this.getModule().getSuperClass+().toString() = "ViewModel" or @@ -50,6 +74,10 @@ class ModelClass extends ModuleBase { } } +/** + * A DataFlow::Node representation that corresponds to any argument passed into a method call + * where the receiver is an instance of ModelClass + */ class ModelClassMethodArgument extends DataFlow::Node { private DataFlow::CallNode call; @@ -59,6 +87,10 @@ class ModelClassMethodArgument extends DataFlow::Node { } } +/** + * Taint tracking config where the source is a weak params access in a controller and the sink + * is a method call of a model class + */ class Configuration extends TaintTracking::Configuration { Configuration() { this = "Configuration" } @@ -70,10 +102,5 @@ class Configuration extends TaintTracking::Configuration { from Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink where config.hasFlowPath(source, sink) -select sink.getNode().(ModelClassMethodArgument), source, sink, "This is bad" -// from WeakParams params -// where -// not params.getEnclosingMethod() instanceof StrongParamsMethod and -// params.getEnclosingModule() instanceof ControllerClass -// select params, -// "By exposing all keys in request parameters or by blindy accessing them, unintended parameters could be used and lead to mass-assignment or have other unexpected side-effects." +select sink.getNode().(ModelClassMethodArgument), source, sink, + "By exposing all keys in request parameters or by blindy accessing them, unintended parameters could be used and lead to mass-assignment or have other unexpected side-effects. It is safer to follow the 'strong parameters' pattern in Rails, which is outlined here: https://api.rubyonrails.org/classes/ActionController/StrongParameters.html" From cf36333082139ae016d3ed1a1c41aa05f9bb50e9 Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Fri, 24 Jun 2022 02:18:48 +0000 Subject: [PATCH 085/505] forgot to finish this test --- ruby/ql/test/query-tests/security/weak-params/WeakParams.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ruby/ql/test/query-tests/security/weak-params/WeakParams.rb b/ruby/ql/test/query-tests/security/weak-params/WeakParams.rb index cc0dc80341f..a2fedd6ef26 100644 --- a/ruby/ql/test/query-tests/security/weak-params/WeakParams.rb +++ b/ruby/ql/test/query-tests/security/weak-params/WeakParams.rb @@ -7,9 +7,12 @@ class TestController < ActionController::Base TestObject.new(request.query_parameters) end + def update + TestObect.update(object_params) + end + # def object_params - p = params.query_parameters params.require(:uuid).permit(:notes) end end \ No newline at end of file From ca074e2275060a7ad41882d922a14418289605f1 Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Fri, 24 Jun 2022 02:19:06 +0000 Subject: [PATCH 086/505] add qhelp file --- .../experimental/weak-params/WeakParams.qhelp | 28 +++++++++++++++++++ .../experimental/weak-params/WeakParams.ql | 6 ++-- 2 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 ruby/ql/src/experimental/weak-params/WeakParams.qhelp diff --git a/ruby/ql/src/experimental/weak-params/WeakParams.qhelp b/ruby/ql/src/experimental/weak-params/WeakParams.qhelp new file mode 100644 index 00000000000..9bccf15d03d --- /dev/null +++ b/ruby/ql/src/experimental/weak-params/WeakParams.qhelp @@ -0,0 +1,28 @@ + + + +

    + Directly checking request parameters without following a strong params + pattern can lead to unintentional avenues for injection attacks. +

    +
    + +

    + Instead of manually checking parameters from the `param` object, it is + recommended that you follow the strong parameters pattern established in + Rails: https://api.rubyonrails.org/classes/ActionController/StrongParameters.html +

    +

    + In the strong parameters pattern, you are able to specify required and allowed + parameters for each action called by your controller methods. This acts as an + additional layer of data validation before being passed along to other areas + of your application, such as the model. +

    +
    + + + + +
    diff --git a/ruby/ql/src/experimental/weak-params/WeakParams.ql b/ruby/ql/src/experimental/weak-params/WeakParams.ql index e3f63b9bfd8..85bb31a7372 100644 --- a/ruby/ql/src/experimental/weak-params/WeakParams.ql +++ b/ruby/ql/src/experimental/weak-params/WeakParams.ql @@ -4,9 +4,10 @@ * @kind path-problem * @problem.severity error * @security-severity 5.0 - * @precision low + * @precision medium * @id rb/weak-params * @tags security + * external/cwe/cwe-223 */ import ruby @@ -64,12 +65,13 @@ class ParamsReference extends ElementReference { } /** - * returns either Model or ViewModel classes with a base class of `ViewModel` or includes `ActionModel::Model`, + * returns either Model or ViewModel classes with a base class of `ViewModel`, `ApplicationRecord` or includes `ActionModel::Model`, * which are required to support the strong parameters pattern */ class ModelClass extends ModuleBase { ModelClass() { this.getModule().getSuperClass+().toString() = "ViewModel" or + this.getModule().getSuperClass+().toString() = "ApplicationRecord" or this.getModule().getSuperClass+().getAnIncludedModule().toString() = "ActionModel::Model" } } From ce2edd4b28c7895292dfa291b130776c846b4105 Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Fri, 24 Jun 2022 02:46:48 +0000 Subject: [PATCH 087/505] style tweaks --- ruby/ql/src/experimental/weak-params/WeakParams.ql | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/ruby/ql/src/experimental/weak-params/WeakParams.ql b/ruby/ql/src/experimental/weak-params/WeakParams.ql index 85bb31a7372..5d77b0e066a 100644 --- a/ruby/ql/src/experimental/weak-params/WeakParams.ql +++ b/ruby/ql/src/experimental/weak-params/WeakParams.ql @@ -16,7 +16,7 @@ import codeql.ruby.TaintTracking import DataFlow::PathGraph /** - * any direct parameters reference that happens outside of a strong params method but inside + * A direct parameters reference that happens outside of a strong params method but inside * of a controller class */ class WeakParams extends Expr { @@ -45,7 +45,7 @@ class StrongParamsMethod extends Method { } /** - * a call to a method that exposes or accesses all parameters from an inbound HTTP request + * A call to a method that exposes or accesses all parameters from an inbound HTTP request */ predicate allParamsAccess(MethodCall call) { call.getMethodName() = "expose_all" or @@ -65,7 +65,7 @@ class ParamsReference extends ElementReference { } /** - * returns either Model or ViewModel classes with a base class of `ViewModel`, `ApplicationRecord` or includes `ActionModel::Model`, + * A Model or ViewModel classes with a base class of `ViewModel`, `ApplicationRecord` or includes `ActionModel::Model`, * which are required to support the strong parameters pattern */ class ModelClass extends ModuleBase { @@ -81,16 +81,15 @@ class ModelClass extends ModuleBase { * where the receiver is an instance of ModelClass */ class ModelClassMethodArgument extends DataFlow::Node { - private DataFlow::CallNode call; ModelClassMethodArgument() { - this = call.getArgument(_) and - call.getExprNode().getNode().getParent+() instanceof ModelClass + exists( DataFlow::CallNode call | this = call.getArgument(_) | + call.getExprNode().getNode().getParent+() instanceof ModelClass ) } } /** - * Taint tracking config where the source is a weak params access in a controller and the sink + * A Taint tracking config where the source is a weak params access in a controller and the sink * is a method call of a model class */ class Configuration extends TaintTracking::Configuration { From 6ea1aad5fc95445067420ffe91382e37824a5505 Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Thu, 23 Jun 2022 22:57:51 -0400 Subject: [PATCH 088/505] more style fixes --- ruby/ql/src/experimental/weak-params/WeakParams.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruby/ql/src/experimental/weak-params/WeakParams.ql b/ruby/ql/src/experimental/weak-params/WeakParams.ql index 5d77b0e066a..f9af2e5c08c 100644 --- a/ruby/ql/src/experimental/weak-params/WeakParams.ql +++ b/ruby/ql/src/experimental/weak-params/WeakParams.ql @@ -45,7 +45,7 @@ class StrongParamsMethod extends Method { } /** - * A call to a method that exposes or accesses all parameters from an inbound HTTP request + * Holds call to a method that exposes or accesses all parameters from an inbound HTTP request */ predicate allParamsAccess(MethodCall call) { call.getMethodName() = "expose_all" or From 03d0f66247d1c0fe5c171b67bd39e9c5d2c145a9 Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Thu, 23 Jun 2022 10:03:43 +0100 Subject: [PATCH 089/505] Ruby: add flow summaries for Pathname class --- ruby/ql/lib/codeql/ruby/frameworks/Core.qll | 1 + .../codeql/ruby/frameworks/core/Pathname.qll | 118 ++++++++++++++++++ .../pathname-flow/pathame-flow.expected | 90 +++++++++++++ .../dataflow/pathname-flow/pathame-flow.ql | 11 ++ .../dataflow/pathname-flow/pathname_flow.rb | 61 +++++++++ 5 files changed, 281 insertions(+) create mode 100644 ruby/ql/lib/codeql/ruby/frameworks/core/Pathname.qll create mode 100644 ruby/ql/test/library-tests/dataflow/pathname-flow/pathame-flow.expected create mode 100644 ruby/ql/test/library-tests/dataflow/pathname-flow/pathame-flow.ql create mode 100644 ruby/ql/test/library-tests/dataflow/pathname-flow/pathname_flow.rb diff --git a/ruby/ql/lib/codeql/ruby/frameworks/Core.qll b/ruby/ql/lib/codeql/ruby/frameworks/Core.qll index 1ef6d8d65e5..b428029c829 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/Core.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/Core.qll @@ -14,6 +14,7 @@ import core.Hash import core.String import core.Regexp import core.IO +import core.Pathname /** * A system command executed via subshell literal syntax. diff --git a/ruby/ql/lib/codeql/ruby/frameworks/core/Pathname.qll b/ruby/ql/lib/codeql/ruby/frameworks/core/Pathname.qll new file mode 100644 index 00000000000..b88ca128283 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/frameworks/core/Pathname.qll @@ -0,0 +1,118 @@ +/** Modeling of the `Pathname` class from the Ruby standard library. */ + +private import codeql.ruby.AST +private import codeql.ruby.ApiGraphs +private import codeql.ruby.DataFlow +private import codeql.ruby.dataflow.FlowSummary +private import codeql.ruby.dataflow.internal.DataFlowDispatch +private import codeql.ruby.controlflow.CfgNodes + +/** + * Modeling of the `Pathname` class from the Ruby standard library. + * + * https://docs.ruby-lang.org/en/3.1/Pathname.html + */ +module Pathname { + /// Flow summary for `Pathname.new`. + private class NewSummary extends SummarizedCallable { + NewSummary() { this = "Pathname.new" } + + override MethodCall getACall() { + result = API::getTopLevelMember("Pathname").getAnInstantiation().getExprNode().getExpr() + } + + override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { + input = "Argument[0]" and + output = "ReturnValue" and + preservesValue = false + } + } + + /// Flow summary for `Pathname#dirname`. + private class DirnameSummary extends SimpleSummarizedCallable { + DirnameSummary() { this = "dirname" } + + override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { + input = "Argument[self]" and + output = "ReturnValue" and + preservesValue = false + } + } + + /// Flow summary for `Pathname#each_filename`. + private class EachFilenameSummary extends SimpleSummarizedCallable { + EachFilenameSummary() { this = "each_filename" } + + override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { + input = "Argument[self]" and + output = "Argument[block].Parameter[0]" and + preservesValue = false + } + } + + /// Flow summary for `Pathname#expand_path`. + private class ExpandPathSummary extends SimpleSummarizedCallable { + ExpandPathSummary() { this = "expand_path" } + + override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { + input = "Argument[self]" and + output = "ReturnValue" and + preservesValue = false + } + } + + /// Flow summary for `Pathname#join`. + private class JoinSummary extends SimpleSummarizedCallable { + JoinSummary() { this = "join" } + + override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { + input = ["Argument[self]", "Argument[any]"] and + output = "ReturnValue" and + preservesValue = false + } + } + + /// Flow summary for `Pathname#parent`. + private class ParentSummary extends SimpleSummarizedCallable { + ParentSummary() { this = "parent" } + + override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { + input = "Argument[self]" and + output = "ReturnValue" and + preservesValue = false + } + } + + /// Flow summary for `Pathname#realpath`. + private class RealpathSummary extends SimpleSummarizedCallable { + RealpathSummary() { this = "realpath" } + + override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { + input = "Argument[self]" and + output = "ReturnValue" and + preservesValue = false + } + } + + /// Flow summary for `Pathname#relative_path_from`. + private class RelativePathFromSummary extends SimpleSummarizedCallable { + RelativePathFromSummary() { this = "relative_path_from" } + + override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { + input = "Argument[self]" and + output = "ReturnValue" and + preservesValue = false + } + } + + /// Flow summary for `Pathname#to_path`. + private class ToPathSummary extends SimpleSummarizedCallable { + ToPathSummary() { this = "to_path" } + + override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { + input = "Argument[self]" and + output = "ReturnValue" and + preservesValue = false + } + } +} diff --git a/ruby/ql/test/library-tests/dataflow/pathname-flow/pathame-flow.expected b/ruby/ql/test/library-tests/dataflow/pathname-flow/pathame-flow.expected new file mode 100644 index 00000000000..8e3a792af46 --- /dev/null +++ b/ruby/ql/test/library-tests/dataflow/pathname-flow/pathame-flow.expected @@ -0,0 +1,90 @@ +failures +edges +| pathname_flow.rb:4:10:4:33 | call to new : | pathname_flow.rb:5:10:5:11 | pn | +| pathname_flow.rb:4:23:4:32 | call to source : | pathname_flow.rb:4:10:4:33 | call to new : | +| pathname_flow.rb:9:6:9:29 | call to new : | pathname_flow.rb:11:7:11:11 | ... + ... | +| pathname_flow.rb:9:19:9:28 | call to source : | pathname_flow.rb:9:6:9:29 | call to new : | +| pathname_flow.rb:10:6:10:29 | call to new : | pathname_flow.rb:11:7:11:11 | ... + ... | +| pathname_flow.rb:10:19:10:28 | call to source : | pathname_flow.rb:10:6:10:29 | call to new : | +| pathname_flow.rb:15:7:15:30 | call to new : | pathname_flow.rb:16:7:16:8 | pn : | +| pathname_flow.rb:15:20:15:29 | call to source : | pathname_flow.rb:15:7:15:30 | call to new : | +| pathname_flow.rb:16:7:16:8 | pn : | pathname_flow.rb:16:7:16:16 | call to dirname | +| pathname_flow.rb:20:6:20:29 | call to new : | pathname_flow.rb:21:2:21:2 | a : | +| pathname_flow.rb:20:19:20:28 | call to source : | pathname_flow.rb:20:6:20:29 | call to new : | +| pathname_flow.rb:21:2:21:2 | a : | pathname_flow.rb:21:22:21:22 | x : | +| pathname_flow.rb:21:22:21:22 | x : | pathname_flow.rb:22:8:22:8 | x | +| pathname_flow.rb:27:6:27:29 | call to new : | pathname_flow.rb:28:7:28:7 | a : | +| pathname_flow.rb:27:19:27:28 | call to source : | pathname_flow.rb:27:6:27:29 | call to new : | +| pathname_flow.rb:28:7:28:7 | a : | pathname_flow.rb:28:7:28:21 | call to expand_path | +| pathname_flow.rb:32:6:32:29 | call to new : | pathname_flow.rb:35:7:35:7 | a : | +| pathname_flow.rb:32:19:32:28 | call to source : | pathname_flow.rb:32:6:32:29 | call to new : | +| pathname_flow.rb:34:6:34:29 | call to new : | pathname_flow.rb:35:17:35:17 | c : | +| pathname_flow.rb:34:19:34:28 | call to source : | pathname_flow.rb:34:6:34:29 | call to new : | +| pathname_flow.rb:35:7:35:7 | a : | pathname_flow.rb:35:7:35:18 | call to join | +| pathname_flow.rb:35:17:35:17 | c : | pathname_flow.rb:35:7:35:18 | call to join | +| pathname_flow.rb:39:6:39:29 | call to new : | pathname_flow.rb:40:7:40:7 | a : | +| pathname_flow.rb:39:19:39:28 | call to source : | pathname_flow.rb:39:6:39:29 | call to new : | +| pathname_flow.rb:40:7:40:7 | a : | pathname_flow.rb:40:7:40:16 | call to parent | +| pathname_flow.rb:44:6:44:29 | call to new : | pathname_flow.rb:45:7:45:7 | a : | +| pathname_flow.rb:44:19:44:28 | call to source : | pathname_flow.rb:44:6:44:29 | call to new : | +| pathname_flow.rb:45:7:45:7 | a : | pathname_flow.rb:45:7:45:18 | call to realpath | +| pathname_flow.rb:49:6:49:29 | call to new : | pathname_flow.rb:50:7:50:7 | a : | +| pathname_flow.rb:49:19:49:28 | call to source : | pathname_flow.rb:49:6:49:29 | call to new : | +| pathname_flow.rb:50:7:50:7 | a : | pathname_flow.rb:50:7:50:38 | call to relative_path_from | +| pathname_flow.rb:54:6:54:29 | call to new : | pathname_flow.rb:55:7:55:7 | a : | +| pathname_flow.rb:54:19:54:28 | call to source : | pathname_flow.rb:54:6:54:29 | call to new : | +| pathname_flow.rb:55:7:55:7 | a : | pathname_flow.rb:55:7:55:15 | call to to_path | +| pathname_flow.rb:59:6:59:29 | call to new : | pathname_flow.rb:60:7:60:7 | a : | +| pathname_flow.rb:59:19:59:28 | call to source : | pathname_flow.rb:59:6:59:29 | call to new : | +| pathname_flow.rb:60:7:60:7 | a : | pathname_flow.rb:60:7:60:12 | call to to_s | +nodes +| pathname_flow.rb:4:10:4:33 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:4:23:4:32 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:5:10:5:11 | pn | semmle.label | pn | +| pathname_flow.rb:9:6:9:29 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:9:19:9:28 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:10:6:10:29 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:10:19:10:28 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:11:7:11:11 | ... + ... | semmle.label | ... + ... | +| pathname_flow.rb:15:7:15:30 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:15:20:15:29 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:16:7:16:8 | pn : | semmle.label | pn : | +| pathname_flow.rb:16:7:16:16 | call to dirname | semmle.label | call to dirname | +| pathname_flow.rb:20:6:20:29 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:20:19:20:28 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:21:2:21:2 | a : | semmle.label | a : | +| pathname_flow.rb:21:22:21:22 | x : | semmle.label | x : | +| pathname_flow.rb:22:8:22:8 | x | semmle.label | x | +| pathname_flow.rb:27:6:27:29 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:27:19:27:28 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:28:7:28:7 | a : | semmle.label | a : | +| pathname_flow.rb:28:7:28:21 | call to expand_path | semmle.label | call to expand_path | +| pathname_flow.rb:32:6:32:29 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:32:19:32:28 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:34:6:34:29 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:34:19:34:28 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:35:7:35:7 | a : | semmle.label | a : | +| pathname_flow.rb:35:7:35:18 | call to join | semmle.label | call to join | +| pathname_flow.rb:35:17:35:17 | c : | semmle.label | c : | +| pathname_flow.rb:39:6:39:29 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:39:19:39:28 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:40:7:40:7 | a : | semmle.label | a : | +| pathname_flow.rb:40:7:40:16 | call to parent | semmle.label | call to parent | +| pathname_flow.rb:44:6:44:29 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:44:19:44:28 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:45:7:45:7 | a : | semmle.label | a : | +| pathname_flow.rb:45:7:45:18 | call to realpath | semmle.label | call to realpath | +| pathname_flow.rb:49:6:49:29 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:49:19:49:28 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:50:7:50:7 | a : | semmle.label | a : | +| pathname_flow.rb:50:7:50:38 | call to relative_path_from | semmle.label | call to relative_path_from | +| pathname_flow.rb:54:6:54:29 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:54:19:54:28 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:55:7:55:7 | a : | semmle.label | a : | +| pathname_flow.rb:55:7:55:15 | call to to_path | semmle.label | call to to_path | +| pathname_flow.rb:59:6:59:29 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:59:19:59:28 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:60:7:60:7 | a : | semmle.label | a : | +| pathname_flow.rb:60:7:60:12 | call to to_s | semmle.label | call to to_s | +subpaths +#select diff --git a/ruby/ql/test/library-tests/dataflow/pathname-flow/pathame-flow.ql b/ruby/ql/test/library-tests/dataflow/pathname-flow/pathame-flow.ql new file mode 100644 index 00000000000..4e812d32daa --- /dev/null +++ b/ruby/ql/test/library-tests/dataflow/pathname-flow/pathame-flow.ql @@ -0,0 +1,11 @@ +/** + * @kind path-problem + */ + +import ruby +import TestUtilities.InlineFlowTest +import PathGraph + +from DataFlow::PathNode source, DataFlow::PathNode sink, DefaultValueFlowConf conf +where conf.hasFlowPath(source, sink) +select sink, source, sink, "$@", source, source.toString() diff --git a/ruby/ql/test/library-tests/dataflow/pathname-flow/pathname_flow.rb b/ruby/ql/test/library-tests/dataflow/pathname-flow/pathname_flow.rb new file mode 100644 index 00000000000..3434b6093b6 --- /dev/null +++ b/ruby/ql/test/library-tests/dataflow/pathname-flow/pathname_flow.rb @@ -0,0 +1,61 @@ +require 'pathname' + +def m_new + pn = Pathname.new(source 'a') + sink pn # $ hasTaintFlow=a +end + +def m_plus + a = Pathname.new(source 'a') + b = Pathname.new(source 'b') + sink(a + b) # $ hasTaintFlow=a $ hasTaintFlow=b +end + +def m_dirname + pn = Pathname.new(source 'a') + sink pn.dirname # $ hasTaintFlow=a +end + +def m_each_filename + a = Pathname.new(source 'a') + a.each_filename do |x| + sink x # $ hasTaintFlow=a + end +end + +def m_expand_path + a = Pathname.new(source 'a') + sink a.expand_path() # $ hasTaintFlow=a +end + +def m_join + a = Pathname.new(source 'a') + b = Pathname.new('foo') + c = Pathname.new(source 'c') + sink a.join(b, c) # $ hasTaintFlow=a $ hasTaintFlow=c +end + +def m_parent + a = Pathname.new(source 'a') + sink a.parent() # $ hasTaintFlow=a +end + +def m_realpath + a = Pathname.new(source 'a') + sink a.realpath() # $ hasTaintFlow=a +end + +def m_relative_path_from + a = Pathname.new(source 'a') + sink a.relative_path_from('/foo/bar') # $ hasTaintFlow=a +end + +def m_to_path + a = Pathname.new(source 'a') + sink a.to_path # $ hasTaintFlow=a +end + +def m_to_s + a = Pathname.new(source 'a') + sink a.to_s # $ hasTaintFlow=a +end \ No newline at end of file From c1515db09c93e65a72a2a6b6af6be9cc464c1ef5 Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Fri, 24 Jun 2022 14:13:02 +0100 Subject: [PATCH 090/505] Ruby: modeling of some file-related concepts for the Pathname class --- .../codeql/ruby/frameworks/core/Pathname.qll | 120 ++++++++++++++-- .../frameworks/pathname/Pathname.expected | 134 ++++++++++++++++++ .../frameworks/pathname/Pathname.ql | 26 ++++ .../frameworks/pathname/Pathname.rb | 42 ++++++ 4 files changed, 312 insertions(+), 10 deletions(-) create mode 100644 ruby/ql/test/library-tests/frameworks/pathname/Pathname.expected create mode 100644 ruby/ql/test/library-tests/frameworks/pathname/Pathname.ql create mode 100644 ruby/ql/test/library-tests/frameworks/pathname/Pathname.rb diff --git a/ruby/ql/lib/codeql/ruby/frameworks/core/Pathname.qll b/ruby/ql/lib/codeql/ruby/frameworks/core/Pathname.qll index b88ca128283..d296d9c75ea 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/core/Pathname.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/core/Pathname.qll @@ -2,10 +2,10 @@ private import codeql.ruby.AST private import codeql.ruby.ApiGraphs +private import codeql.ruby.Concepts private import codeql.ruby.DataFlow private import codeql.ruby.dataflow.FlowSummary private import codeql.ruby.dataflow.internal.DataFlowDispatch -private import codeql.ruby.controlflow.CfgNodes /** * Modeling of the `Pathname` class from the Ruby standard library. @@ -13,7 +13,107 @@ private import codeql.ruby.controlflow.CfgNodes * https://docs.ruby-lang.org/en/3.1/Pathname.html */ module Pathname { - /// Flow summary for `Pathname.new`. + /** + * An instance of the `Pathname` class. For example, in + * + * ```rb + * pn = Pathname.new "foo.txt'" + * puts pn.read + * ``` + * + * there are three `PathnameInstance`s – the call to `Pathname.new`, the + * assignment `pn = ...`, and the read access to `pn` on the second line. + * + * Every `PathnameInstance` is considered to be a `FileNameSource`. + */ + class PathnameInstance extends FileNameSource, DataFlow::Node { + PathnameInstance() { this = pathnameInstance() } + } + + private DataFlow::Node pathnameInstance() { + // A call to `Pathname.new`. + result = API::getTopLevelMember("Pathname").getAnInstantiation() + or + // Class methods on `Pathname` that return a new `Pathname`. + result = API::getTopLevelMember("Pathname").getAMethodCall(["getwd", "pwd",]) + or + // Instance methods on `Pathname` that return a new `Pathname`. + exists(DataFlow::CallNode c | result = c | + c.getReceiver() = pathnameInstance() and + c.getMethodName() = + [ + "+", "/", "basename", "cleanpath", "expand_path", "join", "realpath", + "relative_path_from", "sub", "sub_ext", "to_path" + ] + ) + or + exists(DataFlow::Node inst | + inst = pathnameInstance() and + inst.(DataFlow::LocalSourceNode).flowsTo(result) + ) + } + + /** A call where the receiver is a `Pathname`. */ + class PathnameCall extends DataFlow::CallNode { + PathnameCall() { this.getReceiver() instanceof PathnameInstance } + } + + /** + * A call to `Pathname#open` or `Pathname#opendir`, considered as a + * `FileSystemAccess`. + */ + class PathnameOpen extends FileSystemAccess::Range, PathnameCall { + PathnameOpen() { this.getMethodName() = ["open", "opendir"] } + + override DataFlow::Node getAPathArgument() { result = this.getReceiver() } + } + + /** A call to `Pathname#read`, considered as a `FileSystemReadAccess`. */ + class PathnameRead extends FileSystemReadAccess::Range, PathnameCall { + PathnameRead() { this.getMethodName() = "read" } + + // The path is the receiver (the `Pathname` object). + override DataFlow::Node getAPathArgument() { result = this.getReceiver() } + + // The read data is the return value of the call. + override DataFlow::Node getADataNode() { result = this } + } + + /** A call to `Pathname#write`, considered as a `FileSystemWriteAccess`. */ + class PathnameWrite extends FileSystemWriteAccess::Range, PathnameCall { + PathnameWrite() { this.getMethodName() = "write" } + + // The path is the receiver (the `Pathname` object). + override DataFlow::Node getAPathArgument() { result = this.getReceiver() } + + // The data to write is the 0th argument. + override DataFlow::Node getADataNode() { result = this.getArgument(0) } + } + + /** A call to `Pathname#to_s`, considered as a `FileNameSource`. */ + class PathnameToSFilenameSource extends FileNameSource, PathnameCall { + PathnameToSFilenameSource() { this.getMethodName() = "to_s" } + } + + private class PathnamePermissionModification extends FileSystemPermissionModification::Range, + PathnameCall { + private DataFlow::Node permissionArg; + + PathnamePermissionModification() { + exists(string methodName | this.getMethodName() = methodName | + methodName = ["chmod", "mkdir"] and permissionArg = this.getArgument(0) + or + methodName = "mkpath" and permissionArg = this.getKeywordArgument("mode") + or + methodName = "open" and permissionArg = this.getArgument(1) + // TODO: defaults for optional args? This may depend on the umask + ) + } + + override DataFlow::Node getAPermissionNode() { result = permissionArg } + } + + /** Flow summary for `Pathname.new`. */ private class NewSummary extends SummarizedCallable { NewSummary() { this = "Pathname.new" } @@ -28,7 +128,7 @@ module Pathname { } } - /// Flow summary for `Pathname#dirname`. + /** Flow summary for `Pathname#dirname`. */ private class DirnameSummary extends SimpleSummarizedCallable { DirnameSummary() { this = "dirname" } @@ -39,7 +139,7 @@ module Pathname { } } - /// Flow summary for `Pathname#each_filename`. + /** Flow summary for `Pathname#each_filename`. */ private class EachFilenameSummary extends SimpleSummarizedCallable { EachFilenameSummary() { this = "each_filename" } @@ -50,7 +150,7 @@ module Pathname { } } - /// Flow summary for `Pathname#expand_path`. + /** Flow summary for `Pathname#expand_path`. */ private class ExpandPathSummary extends SimpleSummarizedCallable { ExpandPathSummary() { this = "expand_path" } @@ -61,7 +161,7 @@ module Pathname { } } - /// Flow summary for `Pathname#join`. + /** Flow summary for `Pathname#join`. */ private class JoinSummary extends SimpleSummarizedCallable { JoinSummary() { this = "join" } @@ -72,7 +172,7 @@ module Pathname { } } - /// Flow summary for `Pathname#parent`. + /** Flow summary for `Pathname#parent`. */ private class ParentSummary extends SimpleSummarizedCallable { ParentSummary() { this = "parent" } @@ -83,7 +183,7 @@ module Pathname { } } - /// Flow summary for `Pathname#realpath`. + /** Flow summary for `Pathname#realpath`. */ private class RealpathSummary extends SimpleSummarizedCallable { RealpathSummary() { this = "realpath" } @@ -94,7 +194,7 @@ module Pathname { } } - /// Flow summary for `Pathname#relative_path_from`. + /** Flow summary for `Pathname#relative_path_from`. */ private class RelativePathFromSummary extends SimpleSummarizedCallable { RelativePathFromSummary() { this = "relative_path_from" } @@ -105,7 +205,7 @@ module Pathname { } } - /// Flow summary for `Pathname#to_path`. + /** Flow summary for `Pathname#to_path`. */ private class ToPathSummary extends SimpleSummarizedCallable { ToPathSummary() { this = "to_path" } diff --git a/ruby/ql/test/library-tests/frameworks/pathname/Pathname.expected b/ruby/ql/test/library-tests/frameworks/pathname/Pathname.expected new file mode 100644 index 00000000000..2550c5b4f0d --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/pathname/Pathname.expected @@ -0,0 +1,134 @@ +pathnameInstances +| Pathname.rb:2:1:2:33 | ... = ... | +| Pathname.rb:2:1:2:33 | ... = ... | +| Pathname.rb:2:12:2:33 | call to new | +| Pathname.rb:3:1:3:20 | ... = ... | +| Pathname.rb:3:13:3:20 | foo_path | +| Pathname.rb:4:1:4:8 | foo_path | +| Pathname.rb:6:1:6:29 | ... = ... | +| Pathname.rb:6:1:6:29 | ... = ... | +| Pathname.rb:6:12:6:29 | call to new | +| Pathname.rb:9:1:9:21 | ... = ... | +| Pathname.rb:9:1:9:21 | ... = ... | +| Pathname.rb:9:8:9:21 | call to getwd | +| Pathname.rb:10:1:10:21 | ... = ... | +| Pathname.rb:10:7:10:10 | pwd1 | +| Pathname.rb:10:7:10:21 | ... + ... | +| Pathname.rb:10:14:10:21 | foo_path | +| Pathname.rb:11:1:11:21 | ... = ... | +| Pathname.rb:11:1:11:21 | ... = ... | +| Pathname.rb:11:7:11:10 | pwd1 | +| Pathname.rb:11:7:11:21 | ... / ... | +| Pathname.rb:11:14:11:21 | bar_path | +| Pathname.rb:12:1:12:19 | ... = ... | +| Pathname.rb:12:7:12:10 | pwd1 | +| Pathname.rb:12:7:12:19 | call to basename | +| Pathname.rb:13:1:13:46 | ... = ... | +| Pathname.rb:13:7:13:36 | call to new | +| Pathname.rb:13:7:13:46 | call to cleanpath | +| Pathname.rb:14:1:14:26 | ... = ... | +| Pathname.rb:14:7:14:14 | foo_path | +| Pathname.rb:14:7:14:26 | call to expand_path | +| Pathname.rb:15:1:15:39 | ... = ... | +| Pathname.rb:15:7:15:10 | pwd1 | +| Pathname.rb:15:7:15:39 | call to join | +| Pathname.rb:16:1:16:23 | ... = ... | +| Pathname.rb:16:7:16:14 | foo_path | +| Pathname.rb:16:7:16:23 | call to realpath | +| Pathname.rb:17:1:17:59 | ... = ... | +| Pathname.rb:17:7:17:33 | call to new | +| Pathname.rb:17:7:17:59 | call to relative_path_from | +| Pathname.rb:18:1:18:33 | ... = ... | +| Pathname.rb:18:1:18:33 | ... = ... | +| Pathname.rb:18:7:18:10 | pwd1 | +| Pathname.rb:18:7:18:33 | call to sub | +| Pathname.rb:19:1:19:29 | ... = ... | +| Pathname.rb:19:7:19:14 | foo_path | +| Pathname.rb:19:7:19:29 | call to sub_ext | +| Pathname.rb:20:1:20:22 | ... = ... | +| Pathname.rb:20:7:20:14 | foo_path | +| Pathname.rb:20:7:20:22 | call to to_path | +| Pathname.rb:23:14:23:21 | foo_path | +| Pathname.rb:26:12:26:19 | foo_path | +| Pathname.rb:28:11:28:14 | pwd1 | +| Pathname.rb:32:12:32:19 | foo_path | +| Pathname.rb:35:1:35:8 | foo_path | +| Pathname.rb:38:1:38:8 | foo_path | +| Pathname.rb:39:12:39:19 | foo_path | +| Pathname.rb:41:1:41:3 | p08 | +| Pathname.rb:42:1:42:3 | p01 | +fileSystemAccesses +| Pathname.rb:26:12:26:24 | call to open | Pathname.rb:26:12:26:19 | foo_path | +| Pathname.rb:28:11:28:22 | call to opendir | Pathname.rb:28:11:28:14 | pwd1 | +| Pathname.rb:32:12:32:24 | call to read | Pathname.rb:32:12:32:19 | foo_path | +| Pathname.rb:35:1:35:23 | call to write | Pathname.rb:35:1:35:8 | foo_path | +| Pathname.rb:39:12:39:34 | call to open | Pathname.rb:39:12:39:19 | foo_path | +fileNameSources +| Pathname.rb:2:1:2:33 | ... = ... | +| Pathname.rb:2:1:2:33 | ... = ... | +| Pathname.rb:2:12:2:33 | call to new | +| Pathname.rb:3:1:3:20 | ... = ... | +| Pathname.rb:3:13:3:20 | foo_path | +| Pathname.rb:4:1:4:8 | foo_path | +| Pathname.rb:6:1:6:29 | ... = ... | +| Pathname.rb:6:1:6:29 | ... = ... | +| Pathname.rb:6:12:6:29 | call to new | +| Pathname.rb:9:1:9:21 | ... = ... | +| Pathname.rb:9:1:9:21 | ... = ... | +| Pathname.rb:9:8:9:21 | call to getwd | +| Pathname.rb:10:1:10:21 | ... = ... | +| Pathname.rb:10:7:10:10 | pwd1 | +| Pathname.rb:10:7:10:21 | ... + ... | +| Pathname.rb:10:14:10:21 | foo_path | +| Pathname.rb:11:1:11:21 | ... = ... | +| Pathname.rb:11:1:11:21 | ... = ... | +| Pathname.rb:11:7:11:10 | pwd1 | +| Pathname.rb:11:7:11:21 | ... / ... | +| Pathname.rb:11:14:11:21 | bar_path | +| Pathname.rb:12:1:12:19 | ... = ... | +| Pathname.rb:12:7:12:10 | pwd1 | +| Pathname.rb:12:7:12:19 | call to basename | +| Pathname.rb:13:1:13:46 | ... = ... | +| Pathname.rb:13:7:13:36 | call to new | +| Pathname.rb:13:7:13:46 | call to cleanpath | +| Pathname.rb:14:1:14:26 | ... = ... | +| Pathname.rb:14:7:14:14 | foo_path | +| Pathname.rb:14:7:14:26 | call to expand_path | +| Pathname.rb:15:1:15:39 | ... = ... | +| Pathname.rb:15:7:15:10 | pwd1 | +| Pathname.rb:15:7:15:39 | call to join | +| Pathname.rb:16:1:16:23 | ... = ... | +| Pathname.rb:16:7:16:14 | foo_path | +| Pathname.rb:16:7:16:23 | call to realpath | +| Pathname.rb:17:1:17:59 | ... = ... | +| Pathname.rb:17:7:17:33 | call to new | +| Pathname.rb:17:7:17:59 | call to relative_path_from | +| Pathname.rb:18:1:18:33 | ... = ... | +| Pathname.rb:18:1:18:33 | ... = ... | +| Pathname.rb:18:7:18:10 | pwd1 | +| Pathname.rb:18:7:18:33 | call to sub | +| Pathname.rb:19:1:19:29 | ... = ... | +| Pathname.rb:19:7:19:14 | foo_path | +| Pathname.rb:19:7:19:29 | call to sub_ext | +| Pathname.rb:20:1:20:22 | ... = ... | +| Pathname.rb:20:7:20:14 | foo_path | +| Pathname.rb:20:7:20:22 | call to to_path | +| Pathname.rb:23:14:23:21 | foo_path | +| Pathname.rb:23:14:23:26 | call to to_s | +| Pathname.rb:26:12:26:19 | foo_path | +| Pathname.rb:28:11:28:14 | pwd1 | +| Pathname.rb:32:12:32:19 | foo_path | +| Pathname.rb:35:1:35:8 | foo_path | +| Pathname.rb:38:1:38:8 | foo_path | +| Pathname.rb:39:12:39:19 | foo_path | +| Pathname.rb:41:1:41:3 | p08 | +| Pathname.rb:42:1:42:3 | p01 | +fileSystemReadAccesses +| Pathname.rb:32:12:32:24 | call to read | Pathname.rb:32:12:32:24 | call to read | +fileSystemWriteAccesses +| Pathname.rb:35:1:35:23 | call to write | Pathname.rb:35:16:35:23 | "output" | +fileSystemPermissionModifications +| Pathname.rb:38:1:38:19 | call to chmod | Pathname.rb:38:16:38:19 | 0644 | +| Pathname.rb:39:12:39:34 | call to open | Pathname.rb:39:31:39:34 | 0666 | +| Pathname.rb:41:1:41:14 | call to mkdir | Pathname.rb:41:11:41:14 | 0755 | +| Pathname.rb:42:1:42:22 | call to mkpath | Pathname.rb:42:18:42:21 | 0644 | diff --git a/ruby/ql/test/library-tests/frameworks/pathname/Pathname.ql b/ruby/ql/test/library-tests/frameworks/pathname/Pathname.ql new file mode 100644 index 00000000000..d624cff3aa3 --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/pathname/Pathname.ql @@ -0,0 +1,26 @@ +private import ruby +private import codeql.ruby.Concepts +private import codeql.ruby.DataFlow +private import codeql.ruby.frameworks.core.Pathname + +query predicate pathnameInstances(Pathname::PathnameInstance i) { any() } + +query predicate fileSystemAccesses(FileSystemAccess a, DataFlow::Node p) { + p = a.getAPathArgument() +} + +query predicate fileNameSources(FileNameSource s) { any() } + +query predicate fileSystemReadAccesses(FileSystemReadAccess a, DataFlow::Node d) { + d = a.getADataNode() +} + +query predicate fileSystemWriteAccesses(FileSystemWriteAccess a, DataFlow::Node d) { + d = a.getADataNode() +} + +query predicate fileSystemPermissionModifications( + FileSystemPermissionModification m, DataFlow::Node p +) { + p = m.getAPermissionNode() +} diff --git a/ruby/ql/test/library-tests/frameworks/pathname/Pathname.rb b/ruby/ql/test/library-tests/frameworks/pathname/Pathname.rb new file mode 100644 index 00000000000..ea8f9812281 --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/pathname/Pathname.rb @@ -0,0 +1,42 @@ + +foo_path = Pathname.new "foo.txt" +foo_path2 = foo_path +foo_path + +bar_path = Pathname.new 'bar' + +# All these calls return new `Pathname` instances +pwd1 = Pathname.getwd +p00 = pwd1 + foo_path +p01 = pwd1 / bar_path +p02 = pwd1.basename +p03 = Pathname.new('bar/../baz.txt').cleanpath +p04 = foo_path.expand_path +p05 = pwd1.join 'bar', 'baz', 'qux.txt' +p06 = foo_path.realpath +p07 = Pathname.new('foo/bar.txt').relative_path_from('foo') +p08 = pwd1.sub 'wibble', 'wobble' +p09 = foo_path.sub_ext '.log' +p10 = foo_path.to_path + +# `Pathname#to_s` returns a string that we consider to be a filename source. +foo_string = foo_path.to_s + +# File-system accesses +foo_file = foo_path.open +foo_file.close +pwd_dir = pwd1.opendir +pwd_dir.close + +# Read from a file +foo_data = foo_path.read + +# Write to a file +foo_path.write 'output' + +# Permission modifications +foo_path.chmod 0644 +foo_file = foo_path.open 'w', 0666 +foo_file.close +p08.mkdir 0755 +p01.mkpath(mode: 0644) From 9e4116618ae79337cb9ee3fc6c3157028c551c93 Mon Sep 17 00:00:00 2001 From: Asger F Date: Sat, 18 Jun 2022 19:51:21 +0200 Subject: [PATCH 091/505] JS: Add CaseSensitiveMiddlewarePath query --- .../ql/lib/semmle/javascript/Routing.qll | 12 ++ .../semmle/javascript/frameworks/Express.qll | 23 ++++ .../CWE-178/CaseSensitiveMiddlewarePath.ql | 112 ++++++++++++++++++ .../CaseSensitiveMiddlewarePath.expected | 3 + .../CWE-178/CaseSensitiveMiddlewarePath.qlref | 1 + .../test/query-tests/Security/CWE-178/tst.js | 61 ++++++++++ 6 files changed, 212 insertions(+) create mode 100644 javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.ql create mode 100644 javascript/ql/test/query-tests/Security/CWE-178/CaseSensitiveMiddlewarePath.expected create mode 100644 javascript/ql/test/query-tests/Security/CWE-178/CaseSensitiveMiddlewarePath.qlref create mode 100644 javascript/ql/test/query-tests/Security/CWE-178/tst.js diff --git a/javascript/ql/lib/semmle/javascript/Routing.qll b/javascript/ql/lib/semmle/javascript/Routing.qll index 858ec1ad238..46fdabba6dc 100644 --- a/javascript/ql/lib/semmle/javascript/Routing.qll +++ b/javascript/ql/lib/semmle/javascript/Routing.qll @@ -148,6 +148,18 @@ module Routing { this instanceof MkRouter } + /** + * Like `mayResumeDispatch` but without the assumption that functions with an unknown + * implementation invoke their continuation. + */ + predicate definitelyResumesDispatch() { + this.getLastChild().definitelyResumesDispatch() + or + exists(this.(RouteHandler).getAContinuationInvocation()) + or + this instanceof MkRouter + } + /** Gets the parent of this node, provided that this node may invoke its continuation. */ private Node getContinuationParent() { result = this.getParent() and diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Express.qll b/javascript/ql/lib/semmle/javascript/frameworks/Express.qll index ca9a151e3c6..5a2ad7cc928 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Express.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Express.qll @@ -33,6 +33,11 @@ module Express { or // `app = [new] express.Router()` result = DataFlow::moduleMember("express", "Router").getAnInvocation() + or + exists(DataFlow::SourceNode app | + app.hasUnderlyingType("probot/lib/application", "Application") and + result = app.getAMethodCall("route") + ) } /** @@ -1043,4 +1048,22 @@ module Express { override DataFlow::SourceNode getOutput() { result = this.getCallback(2).getParameter(1) } } + + private class ResumeDispatchRefinement extends Routing::RouteHandler { + ResumeDispatchRefinement() { getFunction() instanceof RouteHandler } + + override predicate mayResumeDispatch() { getAParameter().getName() = "next" } + + override predicate definitelyResumesDispatch() { getAParameter().getName() = "next" } + } + + private class ExpressStaticResumeDispatchRefinement extends Routing::Node { + ExpressStaticResumeDispatchRefinement() { + this = Routing::getNode(DataFlow::moduleMember("express", "static").getACall()) + } + + override predicate mayResumeDispatch() { none() } + + override predicate definitelyResumesDispatch() { none() } + } } diff --git a/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.ql b/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.ql new file mode 100644 index 00000000000..50c5894037e --- /dev/null +++ b/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.ql @@ -0,0 +1,112 @@ +/** + * @name Case-sensitive middleware path + * @description Middleware with case-sensitive paths do not protect endpoints with case-insensitive paths + * @kind problem + * @problem.severity warning + * @security-severity 7.3 + * @precision high + * @id js/case-sensitive-middleware-path + * @tags security + * external/cwe/cwe-178 + */ + +import javascript + +/** + * Converts `s` to upper case, or to lower-case if it was already upper case. + */ +bindingset[s] +string invertCase(string s) { + if s.regexpMatch(".*[a-z].*") then result = s.toUpperCase() else result = s.toLowerCase() +} + +/** + * Holds if `term` distinguishes between upper and lower case letters, assuming the `i` flag is not present. + */ +pragma[inline] +predicate isCaseSensitiveRegExp(RegExpTerm term) { + exists(RegExpConstant const | + const = term.getAChild*() and + const.getValue().regexpMatch(".*[a-zA-Z].*") and + not const.getParent().(RegExpCharacterClass).getAChild().(RegExpConstant).getValue() = + invertCase(const.getValue()) and + not const.getParent*() instanceof RegExpNegativeLookahead and + not const.getParent*() instanceof RegExpNegativeLookbehind + ) +} + +/** + * Gets a string matched by `term`, or part of such a string. + */ +string getExampleString(RegExpTerm term) { + result = term.getAMatchedString() + or + // getAMatchedString does not recurse into sequences. Perform one step manually. + exists(RegExpSequence seq | seq = term | + result = + strictconcat(RegExpTerm child, int i, string text | + child = seq.getChild(i) and + ( + text = child.getAMatchedString() + or + not exists(child.getAMatchedString()) and + text = "" + ) + | + text order by i + ) + ) +} + +string getCaseSensitiveBypassExample(RegExpTerm term) { + result = invertCase(getExampleString(term)) and + result != "" +} + +/** + * Holds if `setup` has a path-argument `arg` referring to the given case-sensitive `regexp`. + */ +predicate isCaseSensitiveMiddleware( + Routing::RouteSetup setup, DataFlow::RegExpCreationNode regexp, DataFlow::Node arg +) { + exists(DataFlow::MethodCallNode call | + setup = Routing::getRouteSetupNode(call) and + ( + setup.definitelyResumesDispatch() + or + // If applied to all HTTP methods, be a bit more lenient in detecting middleware + setup.mayResumeDispatch() and + not exists(setup.getOwnHttpMethod()) + ) and + arg = call.getArgument(0) and + regexp.getAReference().flowsTo(arg) and + isCaseSensitiveRegExp(regexp.getRoot()) and + exists(string flags | + flags = regexp.getFlags() and + not flags.matches("%i%") + ) + ) +} + +predicate isGuardedCaseInsensitiveEndpoint( + Routing::RouteSetup endpoint, Routing::RouteSetup middleware +) { + isCaseSensitiveMiddleware(middleware, _, _) and + exists(DataFlow::MethodCallNode call | + endpoint = Routing::getRouteSetupNode(call) and + endpoint.isGuardedByNode(middleware) and + call.getArgument(0).mayHaveStringValue(_) + ) +} + +from + DataFlow::RegExpCreationNode regexp, Routing::RouteSetup middleware, Routing::RouteSetup endpoint, + DataFlow::Node arg, string example +where + isCaseSensitiveMiddleware(middleware, regexp, arg) and + example = getCaseSensitiveBypassExample(regexp.getRoot()) and + isGuardedCaseInsensitiveEndpoint(endpoint, middleware) and + exists(endpoint.getRelativePath().toLowerCase().indexOf(example.toLowerCase())) +select arg, + "This route uses a case-sensitive path $@, but is guarding a case-insensitive path $@. A path such as '" + + example + "' will bypass the middleware.", regexp, "pattern", endpoint, "here" diff --git a/javascript/ql/test/query-tests/Security/CWE-178/CaseSensitiveMiddlewarePath.expected b/javascript/ql/test/query-tests/Security/CWE-178/CaseSensitiveMiddlewarePath.expected new file mode 100644 index 00000000000..a79b3100a80 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-178/CaseSensitiveMiddlewarePath.expected @@ -0,0 +1,3 @@ +| tst.js:8:9:8:19 | /\\/foo\\/.*/ | This route uses a case-sensitive path $@, but is guarding a case-insensitive path $@. A path such as '/FOO/' will bypass the middleware. | tst.js:8:9:8:19 | /\\/foo\\/.*/ | pattern | tst.js:60:1:61:2 | app.get ... ware\\n}) | here | +| tst.js:14:5:14:28 | new Reg ... (.*)?') | This route uses a case-sensitive path $@, but is guarding a case-insensitive path $@. A path such as '/FOO' will bypass the middleware. | tst.js:14:5:14:28 | new Reg ... (.*)?') | pattern | tst.js:60:1:61:2 | app.get ... ware\\n}) | here | +| tst.js:41:9:41:25 | /\\/foo\\/([0-9]+)/ | This route uses a case-sensitive path $@, but is guarding a case-insensitive path $@. A path such as '/FOO/' will bypass the middleware. | tst.js:41:9:41:25 | /\\/foo\\/([0-9]+)/ | pattern | tst.js:60:1:61:2 | app.get ... ware\\n}) | here | diff --git a/javascript/ql/test/query-tests/Security/CWE-178/CaseSensitiveMiddlewarePath.qlref b/javascript/ql/test/query-tests/Security/CWE-178/CaseSensitiveMiddlewarePath.qlref new file mode 100644 index 00000000000..75705303770 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-178/CaseSensitiveMiddlewarePath.qlref @@ -0,0 +1 @@ +Security/CWE-178/CaseSensitiveMiddlewarePath.ql diff --git a/javascript/ql/test/query-tests/Security/CWE-178/tst.js b/javascript/ql/test/query-tests/Security/CWE-178/tst.js new file mode 100644 index 00000000000..1acb57b16ea --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-178/tst.js @@ -0,0 +1,61 @@ +const express = require('express'); +const app = express(); +const unknown = require('~something/blah'); + +app.all(/\/.*/, unknown()); // OK - does not contain letters +app.all(/\/.*/i, unknown()); // OK + +app.all(/\/foo\/.*/, unknown()); // NOT OK +app.all(/\/foo\/.*/i, unknown()); // OK - case insensitive + +app.use(/\/x\/#\d{6}/, express.static('images/')); // OK - not a middleware + +app.get( + new RegExp('^/foo(.*)?'), // NOT OK - case sensitive + unknown(), + function(req, res, next) { + if (req.params.blah) { + next(); + } + } +); + +app.get( + new RegExp('^/foo(.*)?', 'i'), // OK - case insensitive + unknown(), + function(req, res, next) { + if (req.params.blah) { + next(); + } + } +); + +app.get( + new RegExp('^/foo(.*)?'), // OK - not a middleware + unknown(), + function(req,res) { + res.send('Hello World!'); + } +); + +app.use(/\/foo\/([0-9]+)/, (req, res, next) => { // NOT OK - case sensitive + unknown(req); + next(); +}); + +app.use(/\/foo\/([0-9]+)/i, (req, res, next) => { // OK - case insensitive + unknown(req); + next(); +}); + + +app.use(/\/foo\/([0-9]+)/, (req, res) => { // OK - not middleware + unknown(req, res); +}); + +app.use(/\/foo\/([0-9]+)/i, (req, res) => { // OK - not middleware (also case insensitive) + unknown(req, res); +}); + +app.get('/foo/:param', (req, res) => { // OK - not a middleware +}); From d92430b0e79b60cc1047a4a0e84d3aa889b09eb0 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 24 Jun 2022 12:01:36 +0200 Subject: [PATCH 092/505] JS: Fix FP from char class --- .../CWE-178/CaseSensitiveMiddlewarePath.ql | 17 +++++++++++++---- .../query-tests/Security/CWE-178/charclass.js | 9 +++++++++ 2 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 javascript/ql/test/query-tests/Security/CWE-178/charclass.js diff --git a/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.ql b/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.ql index 50c5894037e..8aca0553c00 100644 --- a/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.ql +++ b/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.ql @@ -20,6 +20,12 @@ string invertCase(string s) { if s.regexpMatch(".*[a-z].*") then result = s.toUpperCase() else result = s.toLowerCase() } +RegExpCharacterClass getEnclosingClass(RegExpTerm term) { + term = result.getAChild() + or + term = result.getAChild().(RegExpRange).getAChild() +} + /** * Holds if `term` distinguishes between upper and lower case letters, assuming the `i` flag is not present. */ @@ -28,7 +34,7 @@ predicate isCaseSensitiveRegExp(RegExpTerm term) { exists(RegExpConstant const | const = term.getAChild*() and const.getValue().regexpMatch(".*[a-zA-Z].*") and - not const.getParent().(RegExpCharacterClass).getAChild().(RegExpConstant).getValue() = + not getEnclosingClass(const).getAChild().(RegExpConstant).getValue() = invertCase(const.getValue()) and not const.getParent*() instanceof RegExpNegativeLookahead and not const.getParent*() instanceof RegExpNegativeLookbehind @@ -59,8 +65,11 @@ string getExampleString(RegExpTerm term) { } string getCaseSensitiveBypassExample(RegExpTerm term) { - result = invertCase(getExampleString(term)) and - result != "" + exists(string example | + example = getExampleString(term) and + result = invertCase(example) and + result != example // getting an example string is approximate; ensure we got a proper case-change example + ) } /** @@ -83,7 +92,7 @@ predicate isCaseSensitiveMiddleware( isCaseSensitiveRegExp(regexp.getRoot()) and exists(string flags | flags = regexp.getFlags() and - not flags.matches("%i%") + not RegExp::isIgnoreCase(flags) ) ) } diff --git a/javascript/ql/test/query-tests/Security/CWE-178/charclass.js b/javascript/ql/test/query-tests/Security/CWE-178/charclass.js new file mode 100644 index 00000000000..f10e0a2d7ab --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-178/charclass.js @@ -0,0 +1,9 @@ +const express = require('express'); +const app = express(); + +app.get(/\/[a-zA-Z]+/, (req, res, next) => { // OK - regexp term is case insensitive + next(); +}); + +app.get('/foo', (req, res) => { +}); From 17d139c87dd9d0aae3db23062ab3febb60fcc75c Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 27 Jun 2022 16:14:30 +0200 Subject: [PATCH 093/505] JS: Add qhelp --- .../CWE-178/CaseSensitiveMiddlewarePath.qhelp | 43 +++++++++++++++++++ .../examples/CaseSensitiveMiddlewarePath.js | 13 ++++++ .../CaseSensitiveMiddlewarePathGood.js | 13 ++++++ 3 files changed, 69 insertions(+) create mode 100644 javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.qhelp create mode 100644 javascript/ql/src/Security/CWE-178/examples/CaseSensitiveMiddlewarePath.js create mode 100644 javascript/ql/src/Security/CWE-178/examples/CaseSensitiveMiddlewarePathGood.js diff --git a/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.qhelp b/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.qhelp new file mode 100644 index 00000000000..bc91fcf9be0 --- /dev/null +++ b/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.qhelp @@ -0,0 +1,43 @@ + + + + +

    +Using a case-sensitive regular expression path in a middleware route enables an attacker to bypass that middleware +when accessing an endpoint with a case-insensitive path. +

    +
    + + +

    +When using a regular expression as a middlware path, make sure the regular expression is +case insensitive by adding the i flag. +

    +
    + + +

    +The following example restricts access to paths in the /admin path to users logged in as +an administrator: +

    + +

    +A path such as /admin/users/45 can only be accessed by an administrator. However, the path +/ADMIN/USERS/45 can be accessed by anyone because the upper-case path doesn't match the case-sensitive regular expression, whereas +Express considers it to match the path string /admin/users. +

    +

    +The issue can be fixed by adding the i flag to the regular expression: +

    + +
    + + +
  • +MDN +Regular Expression Flags. +
  • +
    +
    diff --git a/javascript/ql/src/Security/CWE-178/examples/CaseSensitiveMiddlewarePath.js b/javascript/ql/src/Security/CWE-178/examples/CaseSensitiveMiddlewarePath.js new file mode 100644 index 00000000000..3ae6e071bdd --- /dev/null +++ b/javascript/ql/src/Security/CWE-178/examples/CaseSensitiveMiddlewarePath.js @@ -0,0 +1,13 @@ +const app = require('express')(); + +app.use(/\/admin\/.*/, (req, res, next) => { + if (!req.user.isAdmin) { + res.status(401).send('Unauthorized'); + } else { + next(); + } +}); + +app.get('/admin/users/:id', (req, res) => { + res.send(app.database.users[req.params.id]); +}); diff --git a/javascript/ql/src/Security/CWE-178/examples/CaseSensitiveMiddlewarePathGood.js b/javascript/ql/src/Security/CWE-178/examples/CaseSensitiveMiddlewarePathGood.js new file mode 100644 index 00000000000..1c803bf795d --- /dev/null +++ b/javascript/ql/src/Security/CWE-178/examples/CaseSensitiveMiddlewarePathGood.js @@ -0,0 +1,13 @@ +const app = require('express')(); + +app.use(/\/admin\/.*/i, (req, res, next) => { + if (!req.user.isAdmin) { + res.status(401).send('Unauthorized'); + } else { + next(); + } +}); + +app.get('/admin/users/:id', (req, res) => { + res.send(app.database.users[req.params.id]); +}); From 3c9e7434957b64da523f6b63e1e38e43c9db26ab Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 27 Jun 2022 16:16:38 +0200 Subject: [PATCH 094/505] JS: Add change note --- .../change-notes/2022-06-27-case-sensitive-middleware.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 javascript/ql/src/change-notes/2022-06-27-case-sensitive-middleware.md diff --git a/javascript/ql/src/change-notes/2022-06-27-case-sensitive-middleware.md b/javascript/ql/src/change-notes/2022-06-27-case-sensitive-middleware.md new file mode 100644 index 00000000000..1593596e939 --- /dev/null +++ b/javascript/ql/src/change-notes/2022-06-27-case-sensitive-middleware.md @@ -0,0 +1,6 @@ +--- +category: newQuery +--- + +- A new query "case sensitive middleware path" (`js/case-sensitive-middleware-path`) has been added. + It highlights middleware routes that can be bypassed due to having a case-sensitive regular expression path. From 43bb439b8286cd76730764e367724ce2d2fe0287 Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Mon, 27 Jun 2022 12:03:23 -0700 Subject: [PATCH 095/505] Add version info for running subset of queries --- .../codeql-cli/analyzing-databases-with-the-codeql-cli.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst b/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst index 6c7169634bb..e52cd53e2bd 100644 --- a/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst +++ b/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst @@ -138,7 +138,7 @@ For further information about default suites, see ":ref:`Publishing and using Co Running a subset of queries in a CodeQL pack ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Additionally, you can include a path at the end of a pack specification to run a subset of queries inside the pack. This applies to any command that locates or runs queries within a pack. +Additionally, CodeQL CLI v2.10.0 or later, you can include a path at the end of a pack specification to run a subset of queries inside the pack. This applies to any command that locates or runs queries within a pack. The complete way to specify a set of queries is in the form ``scope/name@range:path``, where: @@ -174,7 +174,7 @@ To analyze your database using the `cpp-security-and-quality.qls` query suite fr codeql database analyze --format=sarif-latest --output=results \ 'codeql/cpp-queries@~0.0.3:codeql-suites/cpp-security-and-quality.qls' - + For more information about CodeQL packs, see :doc:`About CodeQL Packs `. Running query suites @@ -263,7 +263,7 @@ you can include the query help for your custom queries in SARIF files generated After uploading the SARIF file to GitHub, the query help is shown in the code scanning UI for any alerts generated by the custom queries. -From CodeQL CLI 2.7.1 onwards, you can include markdown-rendered query help in SARIF files +From CodeQL CLI v2.7.1 onwards, you can include markdown-rendered query help in SARIF files by providing the ``--sarif-add-query-help`` option when running ``codeql database analyze``. For more information, see `Configuring CodeQL CLI in your CI system `__ From 882000afb3017fcc2814c3dfaa0aa9cd88bdc83e Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Thu, 16 Jun 2022 11:47:34 +0200 Subject: [PATCH 096/505] python: `not` is confusing our logic - added `is_unsafe` - added "negated version" of two tests. These versions do not use `not` and the analysis gets the taint right. --- .../customSanitizer/InlineTaintTest.expected | 16 ++++++------ .../customSanitizer/InlineTaintTest.ql | 8 ++++++ .../customSanitizer/test_logical.py | 25 +++++++++++++++++++ 3 files changed, 42 insertions(+), 7 deletions(-) diff --git a/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/InlineTaintTest.expected b/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/InlineTaintTest.expected index c8c41375538..6e67de96c62 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/InlineTaintTest.expected +++ b/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/InlineTaintTest.expected @@ -6,12 +6,14 @@ isSanitizer | TestTaintTrackingConfiguration | test.py:34:39:34:39 | ControlFlowNode for s | | TestTaintTrackingConfiguration | test.py:52:28:52:28 | ControlFlowNode for s | | TestTaintTrackingConfiguration | test.py:66:10:66:29 | ControlFlowNode for emulated_escaping() | -| TestTaintTrackingConfiguration | test_logical.py:30:28:30:28 | ControlFlowNode for s | -| TestTaintTrackingConfiguration | test_logical.py:45:28:45:28 | ControlFlowNode for s | -| TestTaintTrackingConfiguration | test_logical.py:50:28:50:28 | ControlFlowNode for s | -| TestTaintTrackingConfiguration | test_logical.py:89:28:89:28 | ControlFlowNode for s | -| TestTaintTrackingConfiguration | test_logical.py:100:28:100:28 | ControlFlowNode for s | -| TestTaintTrackingConfiguration | test_logical.py:145:28:145:28 | ControlFlowNode for s | +| TestTaintTrackingConfiguration | test_logical.py:33:28:33:28 | ControlFlowNode for s | +| TestTaintTrackingConfiguration | test_logical.py:48:28:48:28 | ControlFlowNode for s | +| TestTaintTrackingConfiguration | test_logical.py:53:28:53:28 | ControlFlowNode for s | +| TestTaintTrackingConfiguration | test_logical.py:92:28:92:28 | ControlFlowNode for s | +| TestTaintTrackingConfiguration | test_logical.py:103:28:103:28 | ControlFlowNode for s | | TestTaintTrackingConfiguration | test_logical.py:148:28:148:28 | ControlFlowNode for s | -| TestTaintTrackingConfiguration | test_logical.py:155:28:155:28 | ControlFlowNode for s | +| TestTaintTrackingConfiguration | test_logical.py:151:28:151:28 | ControlFlowNode for s | +| TestTaintTrackingConfiguration | test_logical.py:158:28:158:28 | ControlFlowNode for s | +| TestTaintTrackingConfiguration | test_logical.py:176:24:176:24 | ControlFlowNode for s | +| TestTaintTrackingConfiguration | test_logical.py:193:24:193:24 | ControlFlowNode for s | | TestTaintTrackingConfiguration | test_reference.py:31:28:31:28 | ControlFlowNode for s | diff --git a/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/InlineTaintTest.ql b/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/InlineTaintTest.ql index c68bd2d274d..984cf74d036 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/InlineTaintTest.ql +++ b/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/InlineTaintTest.ql @@ -6,6 +6,12 @@ predicate isSafeCheck(DataFlow::GuardNode g, ControlFlowNode node, boolean branc branch = true } +predicate isUnsafeCheck(DataFlow::GuardNode g, ControlFlowNode node, boolean branch) { + g.(CallNode).getNode().getFunc().(Name).getId() in ["is_unsafe", "emulated_is_unsafe"] and + node = g.(CallNode).getAnArg() and + branch = false +} + class CustomSanitizerOverrides extends TestTaintTrackingConfiguration { override predicate isSanitizer(DataFlow::Node node) { exists(Call call | @@ -16,6 +22,8 @@ class CustomSanitizerOverrides extends TestTaintTrackingConfiguration { node.asExpr().(Call).getFunc().(Name).getId() = "emulated_escaping" or node = DataFlow::BarrierGuard::getABarrierNode() + or + node = DataFlow::BarrierGuard::getABarrierNode() } } diff --git a/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/test_logical.py b/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/test_logical.py index a879c3c332c..dc2cc7a5522 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/test_logical.py +++ b/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/test_logical.py @@ -22,6 +22,9 @@ def random_choice(): def is_safe(arg): return arg == "safe" +def is_unsafe(arg): + return arg == TAINTED_STRING + def test_basic(): s = TAINTED_STRING @@ -164,6 +167,15 @@ def test_with_return(): ensure_not_tainted(s) # $ SPURIOUS: tainted +def test_with_return_neg(): + s = TAINTED_STRING + + if is_unsafe(s): + return + + ensure_not_tainted(s) + + def test_with_exception(): s = TAINTED_STRING @@ -172,6 +184,14 @@ def test_with_exception(): ensure_not_tainted(s) # $ SPURIOUS: tainted +def test_with_exception_neg(): + s = TAINTED_STRING + + if is_unsafe(s): + raise Exception("unsafe") + + ensure_not_tainted(s) + # Make tests runable test_basic() @@ -182,7 +202,12 @@ test_tricky() test_nesting_not() test_nesting_not_with_and_true() test_with_return() +test_with_return_neg() try: test_with_exception() except: pass +try: + test_with_exception_neg() +except: + pass From a1fe8a5b2b2d9967c47f3c86db3ce430cc8b6605 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Thu, 16 Jun 2022 13:53:05 +0200 Subject: [PATCH 097/505] python: handle `not` in BarrierGuard in the program ```python if not is_safe(path): return ``` the last node in the `ConditionBlock` is `not is_safe(path)`, so it would never match "a call to is_safe". Thus, guards inside `not` would not be part of `GuardNode` (nor `BarrierGuard`). Now they can. --- .../dataflow/new/internal/DataFlowPublic.qll | 20 +++++++++++++++++-- .../test_string_const_compare.py | 4 ++-- .../customSanitizer/InlineTaintTest.expected | 6 ++++++ .../customSanitizer/test_logical.py | 12 +++++------ 4 files changed, 32 insertions(+), 10 deletions(-) diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll index 406f1fb6b12..3ab007b3657 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll @@ -527,16 +527,32 @@ class StarPatternElementNode extends Node, TStarPatternElementNode { override Location getLocation() { result = consumer.getLocation() } } +ControlFlowNode guardNode(ConditionBlock conditionBlock, boolean flipped) { + result = conditionBlock.getLastNode() and + flipped = false + or + exists(UnaryExprNode notNode | + result = notNode.getOperand() and + notNode.getNode().getOp() instanceof Not + | + notNode = guardNode(conditionBlock, flipped.booleanNot()) + ) +} + /** * A node that controls whether other nodes are evaluated. */ class GuardNode extends ControlFlowNode { ConditionBlock conditionBlock; + boolean flipped; - GuardNode() { this = conditionBlock.getLastNode() } + GuardNode() { this = guardNode(conditionBlock, flipped) } /** Holds if this guard controls block `b` upon evaluating to `branch`. */ - predicate controlsBlock(BasicBlock b, boolean branch) { conditionBlock.controls(b, branch) } + predicate controlsBlock(BasicBlock b, boolean branch) { + branch in [true, false] and + conditionBlock.controls(b, branch.booleanXor(flipped)) + } } /** diff --git a/python/ql/test/experimental/dataflow/tainttracking/commonSanitizer/test_string_const_compare.py b/python/ql/test/experimental/dataflow/tainttracking/commonSanitizer/test_string_const_compare.py index 2fc809cf18f..f1b01f4f84a 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/commonSanitizer/test_string_const_compare.py +++ b/python/ql/test/experimental/dataflow/tainttracking/commonSanitizer/test_string_const_compare.py @@ -50,7 +50,7 @@ def test_non_eq2(): if not ts == "safe": ensure_tainted(ts) # $ tainted else: - ensure_not_tainted(ts) # $ SPURIOUS: tainted + ensure_not_tainted(ts) def test_in_list(): @@ -157,7 +157,7 @@ def test_not_in2(): if not ts in ["safe", "also_safe"]: ensure_tainted(ts) # $ tainted else: - ensure_not_tainted(ts) # $ SPURIOUS: tainted + ensure_not_tainted(ts) def is_safe(x): diff --git a/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/InlineTaintTest.expected b/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/InlineTaintTest.expected index 6e67de96c62..fdad063534b 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/InlineTaintTest.expected +++ b/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/InlineTaintTest.expected @@ -7,13 +7,19 @@ isSanitizer | TestTaintTrackingConfiguration | test.py:52:28:52:28 | ControlFlowNode for s | | TestTaintTrackingConfiguration | test.py:66:10:66:29 | ControlFlowNode for emulated_escaping() | | TestTaintTrackingConfiguration | test_logical.py:33:28:33:28 | ControlFlowNode for s | +| TestTaintTrackingConfiguration | test_logical.py:40:28:40:28 | ControlFlowNode for s | | TestTaintTrackingConfiguration | test_logical.py:48:28:48:28 | ControlFlowNode for s | | TestTaintTrackingConfiguration | test_logical.py:53:28:53:28 | ControlFlowNode for s | | TestTaintTrackingConfiguration | test_logical.py:92:28:92:28 | ControlFlowNode for s | | TestTaintTrackingConfiguration | test_logical.py:103:28:103:28 | ControlFlowNode for s | +| TestTaintTrackingConfiguration | test_logical.py:111:28:111:28 | ControlFlowNode for s | +| TestTaintTrackingConfiguration | test_logical.py:130:28:130:28 | ControlFlowNode for s | +| TestTaintTrackingConfiguration | test_logical.py:137:28:137:28 | ControlFlowNode for s | | TestTaintTrackingConfiguration | test_logical.py:148:28:148:28 | ControlFlowNode for s | | TestTaintTrackingConfiguration | test_logical.py:151:28:151:28 | ControlFlowNode for s | | TestTaintTrackingConfiguration | test_logical.py:158:28:158:28 | ControlFlowNode for s | +| TestTaintTrackingConfiguration | test_logical.py:167:24:167:24 | ControlFlowNode for s | | TestTaintTrackingConfiguration | test_logical.py:176:24:176:24 | ControlFlowNode for s | +| TestTaintTrackingConfiguration | test_logical.py:185:24:185:24 | ControlFlowNode for s | | TestTaintTrackingConfiguration | test_logical.py:193:24:193:24 | ControlFlowNode for s | | TestTaintTrackingConfiguration | test_reference.py:31:28:31:28 | ControlFlowNode for s | diff --git a/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/test_logical.py b/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/test_logical.py index dc2cc7a5522..26e69b8fc05 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/test_logical.py +++ b/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/test_logical.py @@ -37,7 +37,7 @@ def test_basic(): if not is_safe(s): ensure_tainted(s) # $ tainted else: - ensure_not_tainted(s) # $ SPURIOUS: tainted + ensure_not_tainted(s) def test_if_in_depth(): @@ -108,7 +108,7 @@ def test_and(): ensure_tainted(s) # $ tainted else: # cannot be tainted - ensure_not_tainted(s) # $ SPURIOUS: tainted + ensure_not_tainted(s) def test_tricky(): @@ -127,14 +127,14 @@ def test_nesting_not(): s = TAINTED_STRING if not(not(is_safe(s))): - ensure_not_tainted(s) # $ SPURIOUS: tainted + ensure_not_tainted(s) else: ensure_tainted(s) # $ tainted if not(not(not(is_safe(s)))): ensure_tainted(s) # $ tainted else: - ensure_not_tainted(s) # $ SPURIOUS: tainted + ensure_not_tainted(s) # Adding `and True` makes the sanitizer trigger when it would otherwise not. See output in @@ -164,7 +164,7 @@ def test_with_return(): if not is_safe(s): return - ensure_not_tainted(s) # $ SPURIOUS: tainted + ensure_not_tainted(s) def test_with_return_neg(): @@ -182,7 +182,7 @@ def test_with_exception(): if not is_safe(s): raise Exception("unsafe") - ensure_not_tainted(s) # $ SPURIOUS: tainted + ensure_not_tainted(s) def test_with_exception_neg(): s = TAINTED_STRING From 1788507571971a084b6d518033805e2182002a1d Mon Sep 17 00:00:00 2001 From: yoff Date: Mon, 27 Jun 2022 21:00:12 +0000 Subject: [PATCH 098/505] python: add qldoc --- .../dataflow/new/internal/DataFlowPublic.qll | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll index 3ab007b3657..8ab76dc56df 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll @@ -527,10 +527,30 @@ class StarPatternElementNode extends Node, TStarPatternElementNode { override Location getLocation() { result = consumer.getLocation() } } +/** + * Gets a node that controls whether other nodes are evaluated. + * + * In the base case, this is the last node of `conditionBlock`, and `flipped` is `false`. + * This definition accounts for (short circuting) `and`- and `or`-expressions, as the structure + * of basic blocks will reflect their semantics. + * + * However, in the program + * ```python + * if not is_safe(path): + * return + * ``` + * the last node in the `ConditionBlock` is `not is_safe(path)`. + * + * We would like to consider also `is_safe(path)` a guard node, albeit with `flipped` being `true`. + * Thus we recurse through `not`-expressions. + */ ControlFlowNode guardNode(ConditionBlock conditionBlock, boolean flipped) { + // Base case: the last node truly does determine which successor is chosen result = conditionBlock.getLastNode() and flipped = false or + // Recursive case: if a guard node is a `not`-expression, + // the operand is also a guard node, but with inverted polarity. exists(UnaryExprNode notNode | result = notNode.getOperand() and notNode.getNode().getOp() instanceof Not @@ -541,6 +561,9 @@ ControlFlowNode guardNode(ConditionBlock conditionBlock, boolean flipped) { /** * A node that controls whether other nodes are evaluated. + * + * The field `flipped` allows us to match `GuardNode`s underneath + * `not`-expressions and still choose the appropriate branch. */ class GuardNode extends ControlFlowNode { ConditionBlock conditionBlock; From b1251f0c6357db5c2eedbf09f2cf4574173612a8 Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 28 Jun 2022 10:07:57 +0200 Subject: [PATCH 099/505] JS: invertCase -> toOtherCase --- .../ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.ql | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.ql b/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.ql index 8aca0553c00..a497f03f076 100644 --- a/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.ql +++ b/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.ql @@ -16,7 +16,7 @@ import javascript * Converts `s` to upper case, or to lower-case if it was already upper case. */ bindingset[s] -string invertCase(string s) { +string toOtherCase(string s) { if s.regexpMatch(".*[a-z].*") then result = s.toUpperCase() else result = s.toLowerCase() } @@ -35,7 +35,7 @@ predicate isCaseSensitiveRegExp(RegExpTerm term) { const = term.getAChild*() and const.getValue().regexpMatch(".*[a-zA-Z].*") and not getEnclosingClass(const).getAChild().(RegExpConstant).getValue() = - invertCase(const.getValue()) and + toOtherCase(const.getValue()) and not const.getParent*() instanceof RegExpNegativeLookahead and not const.getParent*() instanceof RegExpNegativeLookbehind ) @@ -67,7 +67,7 @@ string getExampleString(RegExpTerm term) { string getCaseSensitiveBypassExample(RegExpTerm term) { exists(string example | example = getExampleString(term) and - result = invertCase(example) and + result = toOtherCase(example) and result != example // getting an example string is approximate; ensure we got a proper case-change example ) } From 9cf48fc8042b6a5c4b9a3befc4956994dc875c27 Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 28 Jun 2022 10:09:56 +0200 Subject: [PATCH 100/505] JS: Clarify that strings are case insensitive by default --- .../ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.qhelp | 1 + 1 file changed, 1 insertion(+) diff --git a/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.qhelp b/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.qhelp index bc91fcf9be0..7acfb8c95da 100644 --- a/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.qhelp +++ b/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.qhelp @@ -7,6 +7,7 @@

    Using a case-sensitive regular expression path in a middleware route enables an attacker to bypass that middleware when accessing an endpoint with a case-insensitive path. +Paths specified using a string are case insensitive, whereas regular expressions are case sensitive by default.

    From fd283970562191a1561d9c93c026850ccad35baf Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 28 Jun 2022 10:10:23 +0200 Subject: [PATCH 101/505] JS: Fix typo --- .../ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.qhelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.qhelp b/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.qhelp index 7acfb8c95da..13e2331bc62 100644 --- a/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.qhelp +++ b/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.qhelp @@ -13,7 +13,7 @@ Paths specified using a string are case insensitive, whereas regular expressions

    -When using a regular expression as a middlware path, make sure the regular expression is +When using a regular expression as a middleware path, make sure the regular expression is case insensitive by adding the i flag.

    From c1a2e2abe06bbdc533bb434150b9c00e7743d07c Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 28 Jun 2022 10:21:33 +0200 Subject: [PATCH 102/505] JS: Rename to `isLikelyCaseSensitiveRegExp` --- .../ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.ql | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.ql b/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.ql index a497f03f076..df3beecfb13 100644 --- a/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.ql +++ b/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.ql @@ -27,10 +27,10 @@ RegExpCharacterClass getEnclosingClass(RegExpTerm term) { } /** - * Holds if `term` distinguishes between upper and lower case letters, assuming the `i` flag is not present. + * Holds if `term` seems to distinguish between upper and lower case letters, assuming the `i` flag is not present. */ pragma[inline] -predicate isCaseSensitiveRegExp(RegExpTerm term) { +predicate isLikelyCaseSensitiveRegExp(RegExpTerm term) { exists(RegExpConstant const | const = term.getAChild*() and const.getValue().regexpMatch(".*[a-zA-Z].*") and @@ -89,7 +89,7 @@ predicate isCaseSensitiveMiddleware( ) and arg = call.getArgument(0) and regexp.getAReference().flowsTo(arg) and - isCaseSensitiveRegExp(regexp.getRoot()) and + isLikelyCaseSensitiveRegExp(regexp.getRoot()) and exists(string flags | flags = regexp.getFlags() and not RegExp::isIgnoreCase(flags) From c33690381eca82f3292f0bd526cba5e9b7780d97 Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 28 Jun 2022 10:21:44 +0200 Subject: [PATCH 103/505] JS: Add explicit 'this' --- javascript/ql/lib/semmle/javascript/frameworks/Express.qll | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Express.qll b/javascript/ql/lib/semmle/javascript/frameworks/Express.qll index 5a2ad7cc928..19616530763 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Express.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Express.qll @@ -1050,11 +1050,11 @@ module Express { } private class ResumeDispatchRefinement extends Routing::RouteHandler { - ResumeDispatchRefinement() { getFunction() instanceof RouteHandler } + ResumeDispatchRefinement() { this.getFunction() instanceof RouteHandler } - override predicate mayResumeDispatch() { getAParameter().getName() = "next" } + override predicate mayResumeDispatch() { this.getAParameter().getName() = "next" } - override predicate definitelyResumesDispatch() { getAParameter().getName() = "next" } + override predicate definitelyResumesDispatch() { this.getAParameter().getName() = "next" } } private class ExpressStaticResumeDispatchRefinement extends Routing::Node { From a7bd2030b670d6d73766b5b7c514af15b2e4044c Mon Sep 17 00:00:00 2001 From: Henry Mercer Date: Tue, 28 Jun 2022 13:52:26 +0100 Subject: [PATCH 104/505] Address review comments --- .../creating-and-working-with-codeql-packs.rst | 2 +- .../codeql-cli/publishing-and-using-codeql-packs.rst | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/codeql/codeql-cli/creating-and-working-with-codeql-packs.rst b/docs/codeql/codeql-cli/creating-and-working-with-codeql-packs.rst index e6a679c89a9..da7a0872803 100644 --- a/docs/codeql/codeql-cli/creating-and-working-with-codeql-packs.rst +++ b/docs/codeql/codeql-cli/creating-and-working-with-codeql-packs.rst @@ -73,6 +73,6 @@ This command downloads all dependencies to the shared cache on the local disk. Note - By default ``codeql pack install`` will install dependencies from the GitHub.com Container registry. + By default ``codeql pack install`` will install dependencies from the Container registry on GitHub.com. You can install dependencies from a GitHub Enterprise Server Container registry by creating a ``qlconfig.yml`` file. For more information, see ":doc:`Publishing and using CodeQL packs `." diff --git a/docs/codeql/codeql-cli/publishing-and-using-codeql-packs.rst b/docs/codeql/codeql-cli/publishing-and-using-codeql-packs.rst index 4e7b8d452ac..e58277d3113 100644 --- a/docs/codeql/codeql-cli/publishing-and-using-codeql-packs.rst +++ b/docs/codeql/codeql-cli/publishing-and-using-codeql-packs.rst @@ -76,10 +76,16 @@ The ``analyze`` command will run the default suite of any specified CodeQL packs Managing packs on GitHub Enterprise Server ------------------------------------------ -By default, CodeQL will download packs from and publish packs to the GitHub.com Container registry. +.. pull-quote:: + + Note + + Managing packs on GitHub Enterprise Server is only available for GitHub Enterprise Server 3.6 and later. + +By default, CodeQL will download packs from and publish packs to the Container registry on GitHub.com. You can manage packs on GitHub Enterprise Server 3.6 and later by creating a ``qlconfig.yml`` file to tell CodeQL which Container registry to use for each pack. Create the ``~/.codeql/qlconfig.yml`` file using your preferred text editor, and add entries to specify which registry to use for each pack name pattern. -For example, the following ``qlconfig.yml`` file associates all packs with the Container registry for the GitHub Enterprise Server at ``GHE_HOSTNAME``, except packs matching ``codeql/*``, which are associated with the GitHub.com Container registry: +For example, the following ``qlconfig.yml`` file associates all packs with the Container registry for the GitHub Enterprise Server at ``GHE_HOSTNAME``, except packs matching ``codeql/*``, which are associated with the Container registry on GitHub.com: .. code-block:: yaml @@ -96,7 +102,7 @@ Authenticating to GitHub Container registries You can download a private pack or publish a pack by authenticating to the appropriate GitHub Container registry. -You can authenticate to the GitHub.com Container registry in two ways: +You can authenticate to the Container registry on GitHub.com in two ways: 1. Pass the ``--github-auth-stdin`` option to the CodeQL CLI, then supply a GitHub Apps token or personal access token via standard input. 2. Set the ``GITHUB_TOKEN`` environment variable to a GitHub Apps token or personal access token. From 82c9b8b49485ff0b54300464fc4b785f9e90a5f0 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Thu, 23 Jun 2022 18:19:30 +0200 Subject: [PATCH 105/505] C++: Ensure only one `Variable` exists for every global variable Depending on the extraction order, before this change there might be multiple `GlobalVariable`s per declared global variable. See the tests in `cpp/ql/test/library-tests/variables/global`. This change ensures that only one of those `GlobalVariable`s is visible to the user if we can locate a unique definition. If not, the old situation persists. Note that an exception needs to be made for templated variables. Here, the definition refers to the non-instantiated template, while a declaration that is not a definition refers to an instantiation. In case the instantiation refers to a template parameter, the mangled names of the template and the instantiation will be identical. This happens for example in the following case: ``` template T x = T(42); // Uninstantiated templated variable template class C { T y = x; // Instantiation using a template parameter }; ``` Since the uninstantiated template and the instantiation are two different entities, we do not unify them as described above. --- cpp/ql/lib/semmle/code/cpp/Element.qll | 4 ++ cpp/ql/lib/semmle/code/cpp/Variable.qll | 3 + .../cpp/internal/ResolveGlobalVariable.qll | 60 +++++++++++++++++++ .../variables/global/variables.expected | 4 -- 4 files changed, 67 insertions(+), 4 deletions(-) create mode 100644 cpp/ql/lib/semmle/code/cpp/internal/ResolveGlobalVariable.qll diff --git a/cpp/ql/lib/semmle/code/cpp/Element.qll b/cpp/ql/lib/semmle/code/cpp/Element.qll index 9e4ef7f5f8d..79df774d80f 100644 --- a/cpp/ql/lib/semmle/code/cpp/Element.qll +++ b/cpp/ql/lib/semmle/code/cpp/Element.qll @@ -6,6 +6,7 @@ import semmle.code.cpp.Location private import semmle.code.cpp.Enclosing private import semmle.code.cpp.internal.ResolveClass +private import semmle.code.cpp.internal.ResolveGlobalVariable /** * Get the `Element` that represents this `@element`. @@ -28,9 +29,12 @@ Element mkElement(@element e) { unresolveElement(result) = e } pragma[inline] @element unresolveElement(Element e) { not result instanceof @usertype and + not result instanceof @variable and result = e or e = resolveClass(result) + or + e = resolveGlobalVariable(result) } /** diff --git a/cpp/ql/lib/semmle/code/cpp/Variable.qll b/cpp/ql/lib/semmle/code/cpp/Variable.qll index 2e3d6bf3ca2..7e188980b9c 100644 --- a/cpp/ql/lib/semmle/code/cpp/Variable.qll +++ b/cpp/ql/lib/semmle/code/cpp/Variable.qll @@ -6,6 +6,7 @@ import semmle.code.cpp.Element import semmle.code.cpp.exprs.Access import semmle.code.cpp.Initializer private import semmle.code.cpp.internal.ResolveClass +private import semmle.code.cpp.internal.ResolveGlobalVariable /** * A C/C++ variable. For example, in the following code there are four @@ -32,6 +33,8 @@ private import semmle.code.cpp.internal.ResolveClass * can have multiple declarations. */ class Variable extends Declaration, @variable { + Variable() { isVariable(underlyingElement(this)) } + override string getAPrimaryQlClass() { result = "Variable" } /** Gets the initializer of this variable, if any. */ diff --git a/cpp/ql/lib/semmle/code/cpp/internal/ResolveGlobalVariable.qll b/cpp/ql/lib/semmle/code/cpp/internal/ResolveGlobalVariable.qll new file mode 100644 index 00000000000..219d45e2991 --- /dev/null +++ b/cpp/ql/lib/semmle/code/cpp/internal/ResolveGlobalVariable.qll @@ -0,0 +1,60 @@ +private predicate hasDefinition(@globalvariable g) { + exists(@var_decl vd | var_decls(vd, g, _, _, _) | var_def(vd)) +} + +pragma[noinline] +private predicate onlyOneCompleteGlobalVariableExistsWithMangledName(@mangledname name) { + strictcount(@globalvariable g | hasDefinition(g) and mangled_name(g, name)) = 1 +} + +/** Holds if `g` is a unique global variable with a definition named `name`. */ +pragma[noinline] +private predicate isGlobalWithMangledNameAndWithDefinition(@mangledname name, @globalvariable g) { + hasDefinition(g) and + mangled_name(g, name) and + onlyOneCompleteGlobalVariableExistsWithMangledName(name) +} + +/** Holds if `g` is a global variable without a definition named `name`. */ +pragma[noinline] +private predicate isGlobalWithMangledNameAndWithoutDefinition(@mangledname name, @globalvariable g) { + not hasDefinition(g) and + mangled_name(g, name) +} + +/** + * Holds if `incomplete` is a global variable without a definition, and there exists + * a unique global variable `complete` with the same name that does have a definition. + */ +private predicate hasTwinWithDefinition(@globalvariable incomplete, @globalvariable complete) { + exists(@mangledname name | + not variable_instantiation(incomplete, complete) and + isGlobalWithMangledNameAndWithoutDefinition(name, incomplete) and + isGlobalWithMangledNameAndWithDefinition(name, complete) + ) +} + +import Cached + +cached +private module Cached { + /** + * If `v` is a global variable without a definition, and there exists a unique + * global variable with the same name that does have a definition, then the + * result is that unique global variable. Otherwise, the result is `v`. + */ + cached + @variable resolveGlobalVariable(@variable v) { + hasTwinWithDefinition(v, result) + or + not hasTwinWithDefinition(v, _) and + result = v + } + + cached + predicate isVariable(@variable v) { + not v instanceof @globalvariable + or + v = resolveGlobalVariable(_) + } +} diff --git a/cpp/ql/test/library-tests/variables/global/variables.expected b/cpp/ql/test/library-tests/variables/global/variables.expected index d66899e62fa..9d022a98264 100644 --- a/cpp/ql/test/library-tests/variables/global/variables.expected +++ b/cpp/ql/test/library-tests/variables/global/variables.expected @@ -4,11 +4,7 @@ | c.c:6:5:6:6 | ls | array of 4 {int} | 1 | | c.c:8:5:8:7 | iss | array of 4 {array of 2 {int}} | 1 | | c.c:12:11:12:11 | i | typedef {int} as "int_alias" | 1 | -| c.h:4:12:4:13 | ks | array of {int} | 1 | -| c.h:8:12:8:14 | iss | array of {array of 2 {int}} | 1 | -| c.h:10:12:10:12 | i | int | 1 | | d.cpp:3:7:3:8 | xs | array of {int} | 1 | -| d.h:3:14:3:15 | xs | array of 2 {int} | 1 | | file://:0:0:0:0 | (unnamed parameter 0) | reference to {const {struct __va_list_tag}} | 1 | | file://:0:0:0:0 | (unnamed parameter 0) | rvalue reference to {struct __va_list_tag} | 1 | | file://:0:0:0:0 | fp_offset | unsigned int | 1 | From a7956ad422d6a8d46128b69824dc87d2b5dccc27 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Fri, 24 Jun 2022 10:15:17 +0200 Subject: [PATCH 106/505] C++: Add change note --- cpp/ql/lib/change-notes/2022-06-24-unique-variable.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 cpp/ql/lib/change-notes/2022-06-24-unique-variable.md diff --git a/cpp/ql/lib/change-notes/2022-06-24-unique-variable.md b/cpp/ql/lib/change-notes/2022-06-24-unique-variable.md new file mode 100644 index 00000000000..e04dde1290a --- /dev/null +++ b/cpp/ql/lib/change-notes/2022-06-24-unique-variable.md @@ -0,0 +1,4 @@ +--- +category: fix +--- +* Under certain circumstances a variable declaration that is not also a definition could be associated with a `Variable` that did not have the definition as a `VariableDeclarationEntry`. This is now fixed, and a unique `Variable` will exist that has both the declaration and the definition as a `VariableDeclarationEntry`. From 0f8ffb12e63389cf30228fe2e197bd5fd5deca88 Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Tue, 28 Jun 2022 09:45:54 -0700 Subject: [PATCH 107/505] Update docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst --- .../codeql-cli/analyzing-databases-with-the-codeql-cli.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst b/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst index e52cd53e2bd..6953d67f81b 100644 --- a/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst +++ b/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst @@ -138,7 +138,7 @@ For further information about default suites, see ":ref:`Publishing and using Co Running a subset of queries in a CodeQL pack ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Additionally, CodeQL CLI v2.10.0 or later, you can include a path at the end of a pack specification to run a subset of queries inside the pack. This applies to any command that locates or runs queries within a pack. +Additionally, CodeQL CLI v2.8.1 or later, you can include a path at the end of a pack specification to run a subset of queries inside the pack. This applies to any command that locates or runs queries within a pack. The complete way to specify a set of queries is in the form ``scope/name@range:path``, where: From c1302a90e0201f98ada5aa0cb0edff76767a26ae Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Wed, 29 Jun 2022 13:16:18 +0100 Subject: [PATCH 108/505] Ruby: use MaD for more precise Pathname flow summaries --- .../codeql/ruby/frameworks/core/Pathname.qll | 164 ++++------ .../pathname-flow/pathame-flow.expected | 289 +++++++++++++----- .../dataflow/pathname-flow/pathname_flow.rb | 124 ++++++-- 3 files changed, 375 insertions(+), 202 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/frameworks/core/Pathname.qll b/ruby/ql/lib/codeql/ruby/frameworks/core/Pathname.qll index d296d9c75ea..e2cea924838 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/core/Pathname.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/core/Pathname.qll @@ -6,6 +6,7 @@ private import codeql.ruby.Concepts private import codeql.ruby.DataFlow private import codeql.ruby.dataflow.FlowSummary private import codeql.ruby.dataflow.internal.DataFlowDispatch +private import codeql.ruby.frameworks.data.ModelsAsData /** * Modeling of the `Pathname` class from the Ruby standard library. @@ -113,106 +114,75 @@ module Pathname { override DataFlow::Node getAPermissionNode() { result = permissionArg } } - /** Flow summary for `Pathname.new`. */ - private class NewSummary extends SummarizedCallable { - NewSummary() { this = "Pathname.new" } - - override MethodCall getACall() { - result = API::getTopLevelMember("Pathname").getAnInstantiation().getExprNode().getExpr() - } - - override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { - input = "Argument[0]" and - output = "ReturnValue" and - preservesValue = false + /** + * Type summaries for the `Pathname` class, i.e. method calls that produce new + * `Pathname` instances. + */ + private class PathnameTypeSummary extends ModelInput::TypeModelCsv { + override predicate row(string row) { + // package1;type1;package2;type2;path + row = + [ + // Pathname.new : Pathname + ";Pathname;;;Member[Pathname].Instance", + // Pathname#+(path) : Pathname + ";Pathname;;Pathname;Method[+].ReturnValue", + // Pathname#/(path) : Pathname + ";Pathname;;Pathname;Method[/].ReturnValue", + // Pathname#basename(path) : Pathname + ";Pathname;;Pathname;Method[basename].ReturnValue", + // Pathname#cleanpath(path) : Pathname + ";Pathname;;Pathname;Method[cleanpath].ReturnValue", + // Pathname#expand_path(path) : Pathname + ";Pathname;;Pathname;Method[expand_path].ReturnValue", + // Pathname#join(path) : Pathname + ";Pathname;;Pathname;Method[join].ReturnValue", + // Pathname#realpath(path) : Pathname + ";Pathname;;Pathname;Method[realpath].ReturnValue", + // Pathname#relative_path_from(path) : Pathname + ";Pathname;;Pathname;Method[relative_path_from].ReturnValue", + // Pathname#sub(path) : Pathname + ";Pathname;;Pathname;Method[sub].ReturnValue", + // Pathname#sub_ext(path) : Pathname + ";Pathname;;Pathname;Method[sub_ext].ReturnValue", + // Pathname#to_path(path) : Pathname + ";Pathname;;Pathname;Method[to_path].ReturnValue", + ] } } - /** Flow summary for `Pathname#dirname`. */ - private class DirnameSummary extends SimpleSummarizedCallable { - DirnameSummary() { this = "dirname" } - - override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { - input = "Argument[self]" and - output = "ReturnValue" and - preservesValue = false - } - } - - /** Flow summary for `Pathname#each_filename`. */ - private class EachFilenameSummary extends SimpleSummarizedCallable { - EachFilenameSummary() { this = "each_filename" } - - override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { - input = "Argument[self]" and - output = "Argument[block].Parameter[0]" and - preservesValue = false - } - } - - /** Flow summary for `Pathname#expand_path`. */ - private class ExpandPathSummary extends SimpleSummarizedCallable { - ExpandPathSummary() { this = "expand_path" } - - override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { - input = "Argument[self]" and - output = "ReturnValue" and - preservesValue = false - } - } - - /** Flow summary for `Pathname#join`. */ - private class JoinSummary extends SimpleSummarizedCallable { - JoinSummary() { this = "join" } - - override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { - input = ["Argument[self]", "Argument[any]"] and - output = "ReturnValue" and - preservesValue = false - } - } - - /** Flow summary for `Pathname#parent`. */ - private class ParentSummary extends SimpleSummarizedCallable { - ParentSummary() { this = "parent" } - - override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { - input = "Argument[self]" and - output = "ReturnValue" and - preservesValue = false - } - } - - /** Flow summary for `Pathname#realpath`. */ - private class RealpathSummary extends SimpleSummarizedCallable { - RealpathSummary() { this = "realpath" } - - override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { - input = "Argument[self]" and - output = "ReturnValue" and - preservesValue = false - } - } - - /** Flow summary for `Pathname#relative_path_from`. */ - private class RelativePathFromSummary extends SimpleSummarizedCallable { - RelativePathFromSummary() { this = "relative_path_from" } - - override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { - input = "Argument[self]" and - output = "ReturnValue" and - preservesValue = false - } - } - - /** Flow summary for `Pathname#to_path`. */ - private class ToPathSummary extends SimpleSummarizedCallable { - ToPathSummary() { this = "to_path" } - - override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { - input = "Argument[self]" and - output = "ReturnValue" and - preservesValue = false + /** Taint flow summaries for the `Pathname` class. */ + private class PathnameTaintSummary extends ModelInput::SummaryModelCsv { + override predicate row(string row) { + row = + [ + // Pathname.new(path) + ";;Member[Pathname].Method[new];Argument[0];ReturnValue;taint", + // Pathname#dirname + ";Pathname;Method[dirname];Argument[self];ReturnValue;taint", + // Pathname#each_filename + ";Pathname;Method[each_filename];Argument[self];Argument[block].Parameter[0];taint", + // Pathname#expand_path + ";Pathname;Method[expand_path];Argument[self];ReturnValue;taint", + // Pathname#join + ";Pathname;Method[join];Argument[self,any];ReturnValue;taint", + // Pathname#parent + ";Pathname;Method[parent];Argument[self];ReturnValue;taint", + // Pathname#realpath + ";Pathname;Method[realpath];Argument[self];ReturnValue;taint", + // Pathname#relative_path_from + ";Pathname;Method[relative_path_from];Argument[self];ReturnValue;taint", + // Pathname#to_path + ";Pathname;Method[to_path];Argument[self];ReturnValue;taint", + // Pathname#basename + ";Pathname;Method[basename];Argument[self];ReturnValue;taint", + // Pathname#cleanpath + ";Pathname;Method[cleanpath];Argument[self];ReturnValue;taint", + // Pathname#sub + ";Pathname;Method[sub];Argument[self];ReturnValue;taint", + // Pathname#sub_ext + ";Pathname;Method[sub_ext];Argument[self];ReturnValue;taint", + ] } } } diff --git a/ruby/ql/test/library-tests/dataflow/pathname-flow/pathame-flow.expected b/ruby/ql/test/library-tests/dataflow/pathname-flow/pathame-flow.expected index 8e3a792af46..a13e860bd04 100644 --- a/ruby/ql/test/library-tests/dataflow/pathname-flow/pathame-flow.expected +++ b/ruby/ql/test/library-tests/dataflow/pathname-flow/pathame-flow.expected @@ -2,89 +2,218 @@ failures edges | pathname_flow.rb:4:10:4:33 | call to new : | pathname_flow.rb:5:10:5:11 | pn | | pathname_flow.rb:4:23:4:32 | call to source : | pathname_flow.rb:4:10:4:33 | call to new : | -| pathname_flow.rb:9:6:9:29 | call to new : | pathname_flow.rb:11:7:11:11 | ... + ... | -| pathname_flow.rb:9:19:9:28 | call to source : | pathname_flow.rb:9:6:9:29 | call to new : | -| pathname_flow.rb:10:6:10:29 | call to new : | pathname_flow.rb:11:7:11:11 | ... + ... | -| pathname_flow.rb:10:19:10:28 | call to source : | pathname_flow.rb:10:6:10:29 | call to new : | -| pathname_flow.rb:15:7:15:30 | call to new : | pathname_flow.rb:16:7:16:8 | pn : | -| pathname_flow.rb:15:20:15:29 | call to source : | pathname_flow.rb:15:7:15:30 | call to new : | -| pathname_flow.rb:16:7:16:8 | pn : | pathname_flow.rb:16:7:16:16 | call to dirname | -| pathname_flow.rb:20:6:20:29 | call to new : | pathname_flow.rb:21:2:21:2 | a : | -| pathname_flow.rb:20:19:20:28 | call to source : | pathname_flow.rb:20:6:20:29 | call to new : | -| pathname_flow.rb:21:2:21:2 | a : | pathname_flow.rb:21:22:21:22 | x : | -| pathname_flow.rb:21:22:21:22 | x : | pathname_flow.rb:22:8:22:8 | x | -| pathname_flow.rb:27:6:27:29 | call to new : | pathname_flow.rb:28:7:28:7 | a : | -| pathname_flow.rb:27:19:27:28 | call to source : | pathname_flow.rb:27:6:27:29 | call to new : | -| pathname_flow.rb:28:7:28:7 | a : | pathname_flow.rb:28:7:28:21 | call to expand_path | -| pathname_flow.rb:32:6:32:29 | call to new : | pathname_flow.rb:35:7:35:7 | a : | -| pathname_flow.rb:32:19:32:28 | call to source : | pathname_flow.rb:32:6:32:29 | call to new : | -| pathname_flow.rb:34:6:34:29 | call to new : | pathname_flow.rb:35:17:35:17 | c : | -| pathname_flow.rb:34:19:34:28 | call to source : | pathname_flow.rb:34:6:34:29 | call to new : | -| pathname_flow.rb:35:7:35:7 | a : | pathname_flow.rb:35:7:35:18 | call to join | -| pathname_flow.rb:35:17:35:17 | c : | pathname_flow.rb:35:7:35:18 | call to join | -| pathname_flow.rb:39:6:39:29 | call to new : | pathname_flow.rb:40:7:40:7 | a : | -| pathname_flow.rb:39:19:39:28 | call to source : | pathname_flow.rb:39:6:39:29 | call to new : | -| pathname_flow.rb:40:7:40:7 | a : | pathname_flow.rb:40:7:40:16 | call to parent | -| pathname_flow.rb:44:6:44:29 | call to new : | pathname_flow.rb:45:7:45:7 | a : | -| pathname_flow.rb:44:19:44:28 | call to source : | pathname_flow.rb:44:6:44:29 | call to new : | -| pathname_flow.rb:45:7:45:7 | a : | pathname_flow.rb:45:7:45:18 | call to realpath | -| pathname_flow.rb:49:6:49:29 | call to new : | pathname_flow.rb:50:7:50:7 | a : | -| pathname_flow.rb:49:19:49:28 | call to source : | pathname_flow.rb:49:6:49:29 | call to new : | -| pathname_flow.rb:50:7:50:7 | a : | pathname_flow.rb:50:7:50:38 | call to relative_path_from | -| pathname_flow.rb:54:6:54:29 | call to new : | pathname_flow.rb:55:7:55:7 | a : | -| pathname_flow.rb:54:19:54:28 | call to source : | pathname_flow.rb:54:6:54:29 | call to new : | -| pathname_flow.rb:55:7:55:7 | a : | pathname_flow.rb:55:7:55:15 | call to to_path | -| pathname_flow.rb:59:6:59:29 | call to new : | pathname_flow.rb:60:7:60:7 | a : | -| pathname_flow.rb:59:19:59:28 | call to source : | pathname_flow.rb:59:6:59:29 | call to new : | -| pathname_flow.rb:60:7:60:7 | a : | pathname_flow.rb:60:7:60:12 | call to to_s | +| pathname_flow.rb:9:7:9:30 | call to new : | pathname_flow.rb:11:8:11:12 | ... + ... | +| pathname_flow.rb:9:20:9:29 | call to source : | pathname_flow.rb:9:7:9:30 | call to new : | +| pathname_flow.rb:10:7:10:30 | call to new : | pathname_flow.rb:11:8:11:12 | ... + ... | +| pathname_flow.rb:10:20:10:29 | call to source : | pathname_flow.rb:10:7:10:30 | call to new : | +| pathname_flow.rb:15:8:15:31 | call to new : | pathname_flow.rb:16:8:16:9 | pn : | +| pathname_flow.rb:15:21:15:30 | call to source : | pathname_flow.rb:15:8:15:31 | call to new : | +| pathname_flow.rb:16:8:16:9 | pn : | pathname_flow.rb:16:8:16:17 | call to dirname | +| pathname_flow.rb:20:7:20:30 | call to new : | pathname_flow.rb:21:3:21:3 | a : | +| pathname_flow.rb:20:20:20:29 | call to source : | pathname_flow.rb:20:7:20:30 | call to new : | +| pathname_flow.rb:21:3:21:3 | a : | pathname_flow.rb:21:23:21:23 | x : | +| pathname_flow.rb:21:23:21:23 | x : | pathname_flow.rb:22:10:22:10 | x | +| pathname_flow.rb:27:7:27:30 | call to new : | pathname_flow.rb:28:8:28:8 | a : | +| pathname_flow.rb:27:20:27:29 | call to source : | pathname_flow.rb:27:7:27:30 | call to new : | +| pathname_flow.rb:28:8:28:8 | a : | pathname_flow.rb:28:8:28:22 | call to expand_path | +| pathname_flow.rb:32:7:32:30 | call to new : | pathname_flow.rb:35:8:35:8 | a : | +| pathname_flow.rb:32:20:32:29 | call to source : | pathname_flow.rb:32:7:32:30 | call to new : | +| pathname_flow.rb:34:7:34:30 | call to new : | pathname_flow.rb:35:18:35:18 | c : | +| pathname_flow.rb:34:20:34:29 | call to source : | pathname_flow.rb:34:7:34:30 | call to new : | +| pathname_flow.rb:35:8:35:8 | a : | pathname_flow.rb:35:8:35:19 | call to join | +| pathname_flow.rb:35:18:35:18 | c : | pathname_flow.rb:35:8:35:19 | call to join | +| pathname_flow.rb:39:7:39:30 | call to new : | pathname_flow.rb:40:8:40:8 | a : | +| pathname_flow.rb:39:20:39:29 | call to source : | pathname_flow.rb:39:7:39:30 | call to new : | +| pathname_flow.rb:40:8:40:8 | a : | pathname_flow.rb:40:8:40:17 | call to parent | +| pathname_flow.rb:44:7:44:30 | call to new : | pathname_flow.rb:45:8:45:8 | a : | +| pathname_flow.rb:44:20:44:29 | call to source : | pathname_flow.rb:44:7:44:30 | call to new : | +| pathname_flow.rb:45:8:45:8 | a : | pathname_flow.rb:45:8:45:19 | call to realpath | +| pathname_flow.rb:49:7:49:30 | call to new : | pathname_flow.rb:50:8:50:8 | a : | +| pathname_flow.rb:49:20:49:29 | call to source : | pathname_flow.rb:49:7:49:30 | call to new : | +| pathname_flow.rb:50:8:50:8 | a : | pathname_flow.rb:50:8:50:39 | call to relative_path_from | +| pathname_flow.rb:54:7:54:30 | call to new : | pathname_flow.rb:55:8:55:8 | a : | +| pathname_flow.rb:54:20:54:29 | call to source : | pathname_flow.rb:54:7:54:30 | call to new : | +| pathname_flow.rb:55:8:55:8 | a : | pathname_flow.rb:55:8:55:16 | call to to_path | +| pathname_flow.rb:59:7:59:30 | call to new : | pathname_flow.rb:60:8:60:8 | a : | +| pathname_flow.rb:59:20:59:29 | call to source : | pathname_flow.rb:59:7:59:30 | call to new : | +| pathname_flow.rb:60:8:60:8 | a : | pathname_flow.rb:60:8:60:13 | call to to_s | +| pathname_flow.rb:64:7:64:30 | call to new : | pathname_flow.rb:66:8:66:8 | b | +| pathname_flow.rb:64:20:64:29 | call to source : | pathname_flow.rb:64:7:64:30 | call to new : | +| pathname_flow.rb:70:7:70:30 | call to new : | pathname_flow.rb:72:8:72:8 | b | +| pathname_flow.rb:70:20:70:29 | call to source : | pathname_flow.rb:70:7:70:30 | call to new : | +| pathname_flow.rb:76:7:76:30 | call to new : | pathname_flow.rb:77:7:77:7 | a : | +| pathname_flow.rb:76:20:76:29 | call to source : | pathname_flow.rb:76:7:76:30 | call to new : | +| pathname_flow.rb:77:7:77:7 | a : | pathname_flow.rb:77:7:77:16 | call to basename : | +| pathname_flow.rb:77:7:77:16 | call to basename : | pathname_flow.rb:78:8:78:8 | b | +| pathname_flow.rb:82:7:82:30 | call to new : | pathname_flow.rb:83:7:83:7 | a : | +| pathname_flow.rb:82:20:82:29 | call to source : | pathname_flow.rb:82:7:82:30 | call to new : | +| pathname_flow.rb:83:7:83:7 | a : | pathname_flow.rb:83:7:83:17 | call to cleanpath : | +| pathname_flow.rb:83:7:83:17 | call to cleanpath : | pathname_flow.rb:84:8:84:8 | b | +| pathname_flow.rb:88:7:88:30 | call to new : | pathname_flow.rb:89:7:89:7 | a : | +| pathname_flow.rb:88:20:88:29 | call to source : | pathname_flow.rb:88:7:88:30 | call to new : | +| pathname_flow.rb:89:7:89:7 | a : | pathname_flow.rb:89:7:89:25 | call to sub : | +| pathname_flow.rb:89:7:89:25 | call to sub : | pathname_flow.rb:90:8:90:8 | b | +| pathname_flow.rb:94:7:94:30 | call to new : | pathname_flow.rb:95:7:95:7 | a : | +| pathname_flow.rb:94:20:94:29 | call to source : | pathname_flow.rb:94:7:94:30 | call to new : | +| pathname_flow.rb:95:7:95:7 | a : | pathname_flow.rb:95:7:95:23 | call to sub_ext : | +| pathname_flow.rb:95:7:95:23 | call to sub_ext : | pathname_flow.rb:96:8:96:8 | b | +| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:104:8:104:8 | b : | +| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:107:8:107:8 | c : | +| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:109:7:109:7 | a : | +| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:112:7:112:7 | a : | +| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:115:7:115:7 | a : | +| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:118:7:118:7 | a : | +| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:121:7:121:7 | a : | +| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:124:7:124:7 | a : | +| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:127:7:127:7 | a : | +| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:130:7:130:7 | a : | +| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:133:7:133:7 | a : | +| pathname_flow.rb:101:20:101:29 | call to source : | pathname_flow.rb:101:7:101:30 | call to new : | +| pathname_flow.rb:104:8:104:8 | b : | pathname_flow.rb:104:8:104:17 | call to realpath | +| pathname_flow.rb:107:8:107:8 | c : | pathname_flow.rb:107:8:107:17 | call to realpath | +| pathname_flow.rb:109:7:109:7 | a : | pathname_flow.rb:109:7:109:16 | call to basename : | +| pathname_flow.rb:109:7:109:16 | call to basename : | pathname_flow.rb:110:8:110:8 | d : | +| pathname_flow.rb:110:8:110:8 | d : | pathname_flow.rb:110:8:110:17 | call to realpath | +| pathname_flow.rb:112:7:112:7 | a : | pathname_flow.rb:112:7:112:17 | call to cleanpath : | +| pathname_flow.rb:112:7:112:17 | call to cleanpath : | pathname_flow.rb:113:8:113:8 | e : | +| pathname_flow.rb:113:8:113:8 | e : | pathname_flow.rb:113:8:113:17 | call to realpath | +| pathname_flow.rb:115:7:115:7 | a : | pathname_flow.rb:115:7:115:19 | call to expand_path : | +| pathname_flow.rb:115:7:115:19 | call to expand_path : | pathname_flow.rb:116:8:116:8 | f : | +| pathname_flow.rb:116:8:116:8 | f : | pathname_flow.rb:116:8:116:17 | call to realpath | +| pathname_flow.rb:118:7:118:7 | a : | pathname_flow.rb:118:7:118:19 | call to join : | +| pathname_flow.rb:118:7:118:19 | call to join : | pathname_flow.rb:119:8:119:8 | g : | +| pathname_flow.rb:119:8:119:8 | g : | pathname_flow.rb:119:8:119:17 | call to realpath | +| pathname_flow.rb:121:7:121:7 | a : | pathname_flow.rb:121:7:121:16 | call to realpath : | +| pathname_flow.rb:121:7:121:16 | call to realpath : | pathname_flow.rb:122:8:122:8 | h : | +| pathname_flow.rb:122:8:122:8 | h : | pathname_flow.rb:122:8:122:17 | call to realpath | +| pathname_flow.rb:124:7:124:7 | a : | pathname_flow.rb:124:7:124:38 | call to relative_path_from : | +| pathname_flow.rb:124:7:124:38 | call to relative_path_from : | pathname_flow.rb:125:8:125:8 | i : | +| pathname_flow.rb:125:8:125:8 | i : | pathname_flow.rb:125:8:125:17 | call to realpath | +| pathname_flow.rb:127:7:127:7 | a : | pathname_flow.rb:127:7:127:25 | call to sub : | +| pathname_flow.rb:127:7:127:25 | call to sub : | pathname_flow.rb:128:8:128:8 | j : | +| pathname_flow.rb:128:8:128:8 | j : | pathname_flow.rb:128:8:128:17 | call to realpath | +| pathname_flow.rb:130:7:130:7 | a : | pathname_flow.rb:130:7:130:23 | call to sub_ext : | +| pathname_flow.rb:130:7:130:23 | call to sub_ext : | pathname_flow.rb:131:8:131:8 | k : | +| pathname_flow.rb:131:8:131:8 | k : | pathname_flow.rb:131:8:131:17 | call to realpath | +| pathname_flow.rb:133:7:133:7 | a : | pathname_flow.rb:133:7:133:15 | call to to_path : | +| pathname_flow.rb:133:7:133:15 | call to to_path : | pathname_flow.rb:134:8:134:8 | l : | +| pathname_flow.rb:134:8:134:8 | l : | pathname_flow.rb:134:8:134:17 | call to realpath | nodes | pathname_flow.rb:4:10:4:33 | call to new : | semmle.label | call to new : | | pathname_flow.rb:4:23:4:32 | call to source : | semmle.label | call to source : | | pathname_flow.rb:5:10:5:11 | pn | semmle.label | pn | -| pathname_flow.rb:9:6:9:29 | call to new : | semmle.label | call to new : | -| pathname_flow.rb:9:19:9:28 | call to source : | semmle.label | call to source : | -| pathname_flow.rb:10:6:10:29 | call to new : | semmle.label | call to new : | -| pathname_flow.rb:10:19:10:28 | call to source : | semmle.label | call to source : | -| pathname_flow.rb:11:7:11:11 | ... + ... | semmle.label | ... + ... | -| pathname_flow.rb:15:7:15:30 | call to new : | semmle.label | call to new : | -| pathname_flow.rb:15:20:15:29 | call to source : | semmle.label | call to source : | -| pathname_flow.rb:16:7:16:8 | pn : | semmle.label | pn : | -| pathname_flow.rb:16:7:16:16 | call to dirname | semmle.label | call to dirname | -| pathname_flow.rb:20:6:20:29 | call to new : | semmle.label | call to new : | -| pathname_flow.rb:20:19:20:28 | call to source : | semmle.label | call to source : | -| pathname_flow.rb:21:2:21:2 | a : | semmle.label | a : | -| pathname_flow.rb:21:22:21:22 | x : | semmle.label | x : | -| pathname_flow.rb:22:8:22:8 | x | semmle.label | x | -| pathname_flow.rb:27:6:27:29 | call to new : | semmle.label | call to new : | -| pathname_flow.rb:27:19:27:28 | call to source : | semmle.label | call to source : | -| pathname_flow.rb:28:7:28:7 | a : | semmle.label | a : | -| pathname_flow.rb:28:7:28:21 | call to expand_path | semmle.label | call to expand_path | -| pathname_flow.rb:32:6:32:29 | call to new : | semmle.label | call to new : | -| pathname_flow.rb:32:19:32:28 | call to source : | semmle.label | call to source : | -| pathname_flow.rb:34:6:34:29 | call to new : | semmle.label | call to new : | -| pathname_flow.rb:34:19:34:28 | call to source : | semmle.label | call to source : | -| pathname_flow.rb:35:7:35:7 | a : | semmle.label | a : | -| pathname_flow.rb:35:7:35:18 | call to join | semmle.label | call to join | -| pathname_flow.rb:35:17:35:17 | c : | semmle.label | c : | -| pathname_flow.rb:39:6:39:29 | call to new : | semmle.label | call to new : | -| pathname_flow.rb:39:19:39:28 | call to source : | semmle.label | call to source : | -| pathname_flow.rb:40:7:40:7 | a : | semmle.label | a : | -| pathname_flow.rb:40:7:40:16 | call to parent | semmle.label | call to parent | -| pathname_flow.rb:44:6:44:29 | call to new : | semmle.label | call to new : | -| pathname_flow.rb:44:19:44:28 | call to source : | semmle.label | call to source : | -| pathname_flow.rb:45:7:45:7 | a : | semmle.label | a : | -| pathname_flow.rb:45:7:45:18 | call to realpath | semmle.label | call to realpath | -| pathname_flow.rb:49:6:49:29 | call to new : | semmle.label | call to new : | -| pathname_flow.rb:49:19:49:28 | call to source : | semmle.label | call to source : | -| pathname_flow.rb:50:7:50:7 | a : | semmle.label | a : | -| pathname_flow.rb:50:7:50:38 | call to relative_path_from | semmle.label | call to relative_path_from | -| pathname_flow.rb:54:6:54:29 | call to new : | semmle.label | call to new : | -| pathname_flow.rb:54:19:54:28 | call to source : | semmle.label | call to source : | -| pathname_flow.rb:55:7:55:7 | a : | semmle.label | a : | -| pathname_flow.rb:55:7:55:15 | call to to_path | semmle.label | call to to_path | -| pathname_flow.rb:59:6:59:29 | call to new : | semmle.label | call to new : | -| pathname_flow.rb:59:19:59:28 | call to source : | semmle.label | call to source : | -| pathname_flow.rb:60:7:60:7 | a : | semmle.label | a : | -| pathname_flow.rb:60:7:60:12 | call to to_s | semmle.label | call to to_s | +| pathname_flow.rb:9:7:9:30 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:9:20:9:29 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:10:7:10:30 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:10:20:10:29 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:11:8:11:12 | ... + ... | semmle.label | ... + ... | +| pathname_flow.rb:15:8:15:31 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:15:21:15:30 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:16:8:16:9 | pn : | semmle.label | pn : | +| pathname_flow.rb:16:8:16:17 | call to dirname | semmle.label | call to dirname | +| pathname_flow.rb:20:7:20:30 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:20:20:20:29 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:21:3:21:3 | a : | semmle.label | a : | +| pathname_flow.rb:21:23:21:23 | x : | semmle.label | x : | +| pathname_flow.rb:22:10:22:10 | x | semmle.label | x | +| pathname_flow.rb:27:7:27:30 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:27:20:27:29 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:28:8:28:8 | a : | semmle.label | a : | +| pathname_flow.rb:28:8:28:22 | call to expand_path | semmle.label | call to expand_path | +| pathname_flow.rb:32:7:32:30 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:32:20:32:29 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:34:7:34:30 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:34:20:34:29 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:35:8:35:8 | a : | semmle.label | a : | +| pathname_flow.rb:35:8:35:19 | call to join | semmle.label | call to join | +| pathname_flow.rb:35:18:35:18 | c : | semmle.label | c : | +| pathname_flow.rb:39:7:39:30 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:39:20:39:29 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:40:8:40:8 | a : | semmle.label | a : | +| pathname_flow.rb:40:8:40:17 | call to parent | semmle.label | call to parent | +| pathname_flow.rb:44:7:44:30 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:44:20:44:29 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:45:8:45:8 | a : | semmle.label | a : | +| pathname_flow.rb:45:8:45:19 | call to realpath | semmle.label | call to realpath | +| pathname_flow.rb:49:7:49:30 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:49:20:49:29 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:50:8:50:8 | a : | semmle.label | a : | +| pathname_flow.rb:50:8:50:39 | call to relative_path_from | semmle.label | call to relative_path_from | +| pathname_flow.rb:54:7:54:30 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:54:20:54:29 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:55:8:55:8 | a : | semmle.label | a : | +| pathname_flow.rb:55:8:55:16 | call to to_path | semmle.label | call to to_path | +| pathname_flow.rb:59:7:59:30 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:59:20:59:29 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:60:8:60:8 | a : | semmle.label | a : | +| pathname_flow.rb:60:8:60:13 | call to to_s | semmle.label | call to to_s | +| pathname_flow.rb:64:7:64:30 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:64:20:64:29 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:66:8:66:8 | b | semmle.label | b | +| pathname_flow.rb:70:7:70:30 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:70:20:70:29 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:72:8:72:8 | b | semmle.label | b | +| pathname_flow.rb:76:7:76:30 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:76:20:76:29 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:77:7:77:7 | a : | semmle.label | a : | +| pathname_flow.rb:77:7:77:16 | call to basename : | semmle.label | call to basename : | +| pathname_flow.rb:78:8:78:8 | b | semmle.label | b | +| pathname_flow.rb:82:7:82:30 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:82:20:82:29 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:83:7:83:7 | a : | semmle.label | a : | +| pathname_flow.rb:83:7:83:17 | call to cleanpath : | semmle.label | call to cleanpath : | +| pathname_flow.rb:84:8:84:8 | b | semmle.label | b | +| pathname_flow.rb:88:7:88:30 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:88:20:88:29 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:89:7:89:7 | a : | semmle.label | a : | +| pathname_flow.rb:89:7:89:25 | call to sub : | semmle.label | call to sub : | +| pathname_flow.rb:90:8:90:8 | b | semmle.label | b | +| pathname_flow.rb:94:7:94:30 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:94:20:94:29 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:95:7:95:7 | a : | semmle.label | a : | +| pathname_flow.rb:95:7:95:23 | call to sub_ext : | semmle.label | call to sub_ext : | +| pathname_flow.rb:96:8:96:8 | b | semmle.label | b | +| pathname_flow.rb:101:7:101:30 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:101:20:101:29 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:104:8:104:8 | b : | semmle.label | b : | +| pathname_flow.rb:104:8:104:17 | call to realpath | semmle.label | call to realpath | +| pathname_flow.rb:107:8:107:8 | c : | semmle.label | c : | +| pathname_flow.rb:107:8:107:17 | call to realpath | semmle.label | call to realpath | +| pathname_flow.rb:109:7:109:7 | a : | semmle.label | a : | +| pathname_flow.rb:109:7:109:16 | call to basename : | semmle.label | call to basename : | +| pathname_flow.rb:110:8:110:8 | d : | semmle.label | d : | +| pathname_flow.rb:110:8:110:17 | call to realpath | semmle.label | call to realpath | +| pathname_flow.rb:112:7:112:7 | a : | semmle.label | a : | +| pathname_flow.rb:112:7:112:17 | call to cleanpath : | semmle.label | call to cleanpath : | +| pathname_flow.rb:113:8:113:8 | e : | semmle.label | e : | +| pathname_flow.rb:113:8:113:17 | call to realpath | semmle.label | call to realpath | +| pathname_flow.rb:115:7:115:7 | a : | semmle.label | a : | +| pathname_flow.rb:115:7:115:19 | call to expand_path : | semmle.label | call to expand_path : | +| pathname_flow.rb:116:8:116:8 | f : | semmle.label | f : | +| pathname_flow.rb:116:8:116:17 | call to realpath | semmle.label | call to realpath | +| pathname_flow.rb:118:7:118:7 | a : | semmle.label | a : | +| pathname_flow.rb:118:7:118:19 | call to join : | semmle.label | call to join : | +| pathname_flow.rb:119:8:119:8 | g : | semmle.label | g : | +| pathname_flow.rb:119:8:119:17 | call to realpath | semmle.label | call to realpath | +| pathname_flow.rb:121:7:121:7 | a : | semmle.label | a : | +| pathname_flow.rb:121:7:121:16 | call to realpath : | semmle.label | call to realpath : | +| pathname_flow.rb:122:8:122:8 | h : | semmle.label | h : | +| pathname_flow.rb:122:8:122:17 | call to realpath | semmle.label | call to realpath | +| pathname_flow.rb:124:7:124:7 | a : | semmle.label | a : | +| pathname_flow.rb:124:7:124:38 | call to relative_path_from : | semmle.label | call to relative_path_from : | +| pathname_flow.rb:125:8:125:8 | i : | semmle.label | i : | +| pathname_flow.rb:125:8:125:17 | call to realpath | semmle.label | call to realpath | +| pathname_flow.rb:127:7:127:7 | a : | semmle.label | a : | +| pathname_flow.rb:127:7:127:25 | call to sub : | semmle.label | call to sub : | +| pathname_flow.rb:128:8:128:8 | j : | semmle.label | j : | +| pathname_flow.rb:128:8:128:17 | call to realpath | semmle.label | call to realpath | +| pathname_flow.rb:130:7:130:7 | a : | semmle.label | a : | +| pathname_flow.rb:130:7:130:23 | call to sub_ext : | semmle.label | call to sub_ext : | +| pathname_flow.rb:131:8:131:8 | k : | semmle.label | k : | +| pathname_flow.rb:131:8:131:17 | call to realpath | semmle.label | call to realpath | +| pathname_flow.rb:133:7:133:7 | a : | semmle.label | a : | +| pathname_flow.rb:133:7:133:15 | call to to_path : | semmle.label | call to to_path : | +| pathname_flow.rb:134:8:134:8 | l : | semmle.label | l : | +| pathname_flow.rb:134:8:134:17 | call to realpath | semmle.label | call to realpath | subpaths #select diff --git a/ruby/ql/test/library-tests/dataflow/pathname-flow/pathname_flow.rb b/ruby/ql/test/library-tests/dataflow/pathname-flow/pathname_flow.rb index 3434b6093b6..dab4adec6c6 100644 --- a/ruby/ql/test/library-tests/dataflow/pathname-flow/pathname_flow.rb +++ b/ruby/ql/test/library-tests/dataflow/pathname-flow/pathname_flow.rb @@ -6,56 +6,130 @@ def m_new end def m_plus - a = Pathname.new(source 'a') - b = Pathname.new(source 'b') - sink(a + b) # $ hasTaintFlow=a $ hasTaintFlow=b + a = Pathname.new(source 'a') + b = Pathname.new(source 'b') + sink(a + b) # $ hasTaintFlow=a $ hasTaintFlow=b end def m_dirname - pn = Pathname.new(source 'a') - sink pn.dirname # $ hasTaintFlow=a + pn = Pathname.new(source 'a') + sink pn.dirname # $ hasTaintFlow=a end def m_each_filename - a = Pathname.new(source 'a') - a.each_filename do |x| - sink x # $ hasTaintFlow=a - end + a = Pathname.new(source 'a') + a.each_filename do |x| + sink x # $ hasTaintFlow=a + end end def m_expand_path - a = Pathname.new(source 'a') - sink a.expand_path() # $ hasTaintFlow=a + a = Pathname.new(source 'a') + sink a.expand_path() # $ hasTaintFlow=a end def m_join - a = Pathname.new(source 'a') - b = Pathname.new('foo') - c = Pathname.new(source 'c') - sink a.join(b, c) # $ hasTaintFlow=a $ hasTaintFlow=c + a = Pathname.new(source 'a') + b = Pathname.new('foo') + c = Pathname.new(source 'c') + sink a.join(b, c) # $ hasTaintFlow=a $ hasTaintFlow=c end def m_parent - a = Pathname.new(source 'a') - sink a.parent() # $ hasTaintFlow=a + a = Pathname.new(source 'a') + sink a.parent() # $ hasTaintFlow=a end def m_realpath - a = Pathname.new(source 'a') - sink a.realpath() # $ hasTaintFlow=a + a = Pathname.new(source 'a') + sink a.realpath() # $ hasTaintFlow=a end def m_relative_path_from - a = Pathname.new(source 'a') - sink a.relative_path_from('/foo/bar') # $ hasTaintFlow=a + a = Pathname.new(source 'a') + sink a.relative_path_from('/foo/bar') # $ hasTaintFlow=a end def m_to_path - a = Pathname.new(source 'a') - sink a.to_path # $ hasTaintFlow=a + a = Pathname.new(source 'a') + sink a.to_path # $ hasTaintFlow=a end def m_to_s - a = Pathname.new(source 'a') - sink a.to_s # $ hasTaintFlow=a + a = Pathname.new(source 'a') + sink a.to_s # $ hasTaintFlow=a +end + +def m_plus + a = Pathname.new(source 'a') + b = a + 'foo' + sink b # $ hasTaintFlow=a +end + +def m_slash + a = Pathname.new(source 'a') + b = a / 'foo' + sink b # $ hasTaintFlow=a +end + +def m_basename + a = Pathname.new(source 'a') + b = a.basename + sink b # $ hasTaintFlow=a +end + +def m_cleanpath + a = Pathname.new(source 'a') + b = a.cleanpath + sink b # $ hasTaintFlow=a +end + +def m_sub + a = Pathname.new(source 'a') + b = a.sub('foo', 'bar') + sink b # $ hasTaintFlow=a +end + +def m_sub_ext + a = Pathname.new(source 'a') + b = a.sub_ext('.txt') + sink b # $ hasTaintFlow=a +end + +# Test flow through intermediate pathnames +def intermediate_pathnames + a = Pathname.new(source 'a') + + b = a + 'foo' + sink b.realpath # $ hasTaintFlow=a + + c = a / 'foo' + sink c.realpath # $ hasTaintFlow=a + + d = a.basename + sink d.realpath # $ hasTaintFlow=a + + e = a.cleanpath + sink e.realpath # $ hasTaintFlow=a + + f = a.expand_path + sink f.realpath # $ hasTaintFlow=a + + g = a.join('foo') + sink g.realpath # $ hasTaintFlow=a + + h = a.realpath + sink h.realpath # $ hasTaintFlow=a + + i = a.relative_path_from('/foo/bar') + sink i.realpath # $ hasTaintFlow=a + + j = a.sub('foo', 'bar') + sink j.realpath # $ hasTaintFlow=a + + k = a.sub_ext('.txt') + sink k.realpath # $ hasTaintFlow=a + + l = a.to_path + sink l.realpath # $ hasTaintFlow=a end \ No newline at end of file From 4dcec2b98ca49e66935976c2816345b396ce954b Mon Sep 17 00:00:00 2001 From: Henry Mercer Date: Wed, 29 Jun 2022 17:49:59 +0100 Subject: [PATCH 109/505] Apply suggestions from code review Co-authored-by: Felicity Chapman Co-authored-by: Andrew Eisenberg --- .../publishing-and-using-codeql-packs.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/codeql/codeql-cli/publishing-and-using-codeql-packs.rst b/docs/codeql/codeql-cli/publishing-and-using-codeql-packs.rst index e58277d3113..c3a3a2d9ed2 100644 --- a/docs/codeql/codeql-cli/publishing-and-using-codeql-packs.rst +++ b/docs/codeql/codeql-cli/publishing-and-using-codeql-packs.rst @@ -73,27 +73,27 @@ The ``analyze`` command will run the default suite of any specified CodeQL packs codeql analyze / / -Managing packs on GitHub Enterprise Server +Working with CodeQL packs on GitHub Enterprise Server ------------------------------------------ .. pull-quote:: Note - Managing packs on GitHub Enterprise Server is only available for GitHub Enterprise Server 3.6 and later. + The Container registry for GitHub Enterprise Server supports CodeQL query packs from GitHub Enterprise Server 3.6 onward. -By default, CodeQL will download packs from and publish packs to the Container registry on GitHub.com. -You can manage packs on GitHub Enterprise Server 3.6 and later by creating a ``qlconfig.yml`` file to tell CodeQL which Container registry to use for each pack. -Create the ``~/.codeql/qlconfig.yml`` file using your preferred text editor, and add entries to specify which registry to use for each pack name pattern. +By default, the CodeQL CLI expects to download CodeQL packs from and publish packs to the Container registry on GitHub.com. However, you can also work with CodeQL packs in a Container registry on GitHub Enterprise Server 3.6, and later, by creating a ``qlconfig.yml`` file to tell the CLI which Container registry to use for each pack. + +Create a ``~/.codeql/qlconfig.yml`` file using your preferred text editor, and add entries to specify which registry to use for one or more package name patterns. For example, the following ``qlconfig.yml`` file associates all packs with the Container registry for the GitHub Enterprise Server at ``GHE_HOSTNAME``, except packs matching ``codeql/*``, which are associated with the Container registry on GitHub.com: .. code-block:: yaml registries: - packages: '*' - url: https://containers.GHE_HOSTNAME/v2/ + url: https://containers.GHE_HOSTNAME/v2/ - packages: 'codeql/*' - url: https://ghcr.io/v2/ + url: https://ghcr.io/v2/ You can now use ``codeql pack publish``, ``codeql pack download``, and ``codeql database analyze`` to manage packs on GitHub Enterprise Server. @@ -107,7 +107,7 @@ You can authenticate to the Container registry on GitHub.com in two ways: 1. Pass the ``--github-auth-stdin`` option to the CodeQL CLI, then supply a GitHub Apps token or personal access token via standard input. 2. Set the ``GITHUB_TOKEN`` environment variable to a GitHub Apps token or personal access token. -Similarly, you can authenticate to a GHES Container registry, or authenticate to multiple registries simultaneously (for example to download or analyze private packs from multiple registries) in two ways: +Similarly, you can authenticate to a GHES Container registry, or authenticate to multiple registries simultaneously (for example, to download or run private packs from multiple registries) in two ways: 1. Pass the ``--registries-auth-stdin`` option to the CodeQL CLI, then supply a registry authentication string via standard input. 2. Set the ``CODEQL_REGISTRIES_AUTH`` environment variable to a registry authentication string. From 41244180b3bcdfea10d063d2cd7ce7b168aad67f Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Wed, 29 Jun 2022 10:18:13 -0700 Subject: [PATCH 110/505] Apply suggestions from code review Co-authored-by: Felicity Chapman --- .../analyzing-databases-with-the-codeql-cli.rst | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst b/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst index 6953d67f81b..bb428f2c00d 100644 --- a/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst +++ b/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst @@ -138,7 +138,7 @@ For further information about default suites, see ":ref:`Publishing and using Co Running a subset of queries in a CodeQL pack ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Additionally, CodeQL CLI v2.8.1 or later, you can include a path at the end of a pack specification to run a subset of queries inside the pack. This applies to any command that locates or runs queries within a pack. +If you are using CodeQL CLI v2.8.1 or later, you can include a path at the end of a pack specification to run a subset of queries inside the pack. This applies to any command that locates or runs queries within a pack. The complete way to specify a set of queries is in the form ``scope/name@range:path``, where: @@ -146,20 +146,21 @@ The complete way to specify a set of queries is in the form ``scope/name@range:p - ``range`` is a `semver range `_. - ``path`` is a file system path to a single query, a directory containing queries, or a query suite file. -If a ``scope/name`` is specified, the ``range`` and ``path`` are -optional. A missing ``range`` implies the latest version of the -specified pack. A missing ``path`` implies the default query suite -of the specified pack. +When you specify a ``scope/name``, the ``range`` and ``path`` are +optional. If you omit a ``range`` then the latest version of the +specified pack is used. If you omit a ``path`` then the default query suite +of the specified pack is used. The ``path`` can be one of a ``*.ql`` query file, a directory containing one or more queries, or a ``.qls`` query suite file. If -there is no pack name specified, then a ``path`` must be provided, -and will be interpreted relative to the current working directory +you omit a pack name, then you must provide a ``path``, +which will be interpreted relative to the working directory of the current process. -If a ``scope/name`` and ``path`` are specified, then the ``path`` cannot +If you specify a ``scope/name`` and ``path``, then the ``path`` cannot be absolute. It is considered relative to the root of the CodeQL pack. + To analyze a database using all queries in the `experimental/Security` folder within the `codeql/cpp-queries` CodeQL pack you can use:: codeql database analyze --format=sarif-latest --output=results \ From 02dd933e5f51932c45893f5fad18dea1c1b53c1f Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Thu, 30 Jun 2022 09:32:23 +0100 Subject: [PATCH 111/505] Ruby: move Pathname from core to stdlib --- ruby/ql/lib/codeql/ruby/frameworks/Core.qll | 1 - ruby/ql/lib/codeql/ruby/frameworks/Stdlib.qll | 1 + .../ql/lib/codeql/ruby/frameworks/{core => stdlib}/Pathname.qll | 0 ruby/ql/test/library-tests/frameworks/pathname/Pathname.ql | 2 +- 4 files changed, 2 insertions(+), 2 deletions(-) rename ruby/ql/lib/codeql/ruby/frameworks/{core => stdlib}/Pathname.qll (100%) diff --git a/ruby/ql/lib/codeql/ruby/frameworks/Core.qll b/ruby/ql/lib/codeql/ruby/frameworks/Core.qll index b428029c829..1ef6d8d65e5 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/Core.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/Core.qll @@ -14,7 +14,6 @@ import core.Hash import core.String import core.Regexp import core.IO -import core.Pathname /** * A system command executed via subshell literal syntax. diff --git a/ruby/ql/lib/codeql/ruby/frameworks/Stdlib.qll b/ruby/ql/lib/codeql/ruby/frameworks/Stdlib.qll index 11c993b1170..f735f9daf8b 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/Stdlib.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/Stdlib.qll @@ -4,3 +4,4 @@ import stdlib.Open3 import stdlib.Logger +import stdlib.Pathname diff --git a/ruby/ql/lib/codeql/ruby/frameworks/core/Pathname.qll b/ruby/ql/lib/codeql/ruby/frameworks/stdlib/Pathname.qll similarity index 100% rename from ruby/ql/lib/codeql/ruby/frameworks/core/Pathname.qll rename to ruby/ql/lib/codeql/ruby/frameworks/stdlib/Pathname.qll diff --git a/ruby/ql/test/library-tests/frameworks/pathname/Pathname.ql b/ruby/ql/test/library-tests/frameworks/pathname/Pathname.ql index d624cff3aa3..71150ae5376 100644 --- a/ruby/ql/test/library-tests/frameworks/pathname/Pathname.ql +++ b/ruby/ql/test/library-tests/frameworks/pathname/Pathname.ql @@ -1,7 +1,7 @@ private import ruby private import codeql.ruby.Concepts private import codeql.ruby.DataFlow -private import codeql.ruby.frameworks.core.Pathname +private import codeql.ruby.frameworks.stdlib.Pathname query predicate pathnameInstances(Pathname::PathnameInstance i) { any() } From 3bb51c2643f331704d06d06d7a297ed7b9863c0b Mon Sep 17 00:00:00 2001 From: Henry Mercer Date: Thu, 30 Jun 2022 17:07:42 +0100 Subject: [PATCH 112/505] Fix rst header --- docs/codeql/codeql-cli/publishing-and-using-codeql-packs.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/codeql/codeql-cli/publishing-and-using-codeql-packs.rst b/docs/codeql/codeql-cli/publishing-and-using-codeql-packs.rst index c3a3a2d9ed2..3d5ca470644 100644 --- a/docs/codeql/codeql-cli/publishing-and-using-codeql-packs.rst +++ b/docs/codeql/codeql-cli/publishing-and-using-codeql-packs.rst @@ -74,7 +74,7 @@ The ``analyze`` command will run the default suite of any specified CodeQL packs codeql analyze / / Working with CodeQL packs on GitHub Enterprise Server ------------------------------------------- +----------------------------------------------------- .. pull-quote:: From 92a9738bd5f25543667049b7e8e75284306b4991 Mon Sep 17 00:00:00 2001 From: Henry Mercer Date: Thu, 30 Jun 2022 17:32:00 +0100 Subject: [PATCH 113/505] Docs: Fix precedence of `registries` list --- .../codeql-cli/publishing-and-using-codeql-packs.rst | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/codeql/codeql-cli/publishing-and-using-codeql-packs.rst b/docs/codeql/codeql-cli/publishing-and-using-codeql-packs.rst index 3d5ca470644..67e90b5ba3f 100644 --- a/docs/codeql/codeql-cli/publishing-and-using-codeql-packs.rst +++ b/docs/codeql/codeql-cli/publishing-and-using-codeql-packs.rst @@ -90,10 +90,13 @@ For example, the following ``qlconfig.yml`` file associates all packs with the C .. code-block:: yaml registries: - - packages: '*' - url: https://containers.GHE_HOSTNAME/v2/ - packages: 'codeql/*' url: https://ghcr.io/v2/ + - packages: '*' + url: https://containers.GHE_HOSTNAME/v2/ + +The CodeQL CLI will determine which registry to use for a given package name by finding the first item in the ``registries`` list with a ``packages`` property that matches that package name. +This means that you'll generally want to define the most specific package name patterns first. You can now use ``codeql pack publish``, ``codeql pack download``, and ``codeql database analyze`` to manage packs on GitHub Enterprise Server. From 9b424ac8b2a43d1f156b93c52fc8d4d758957400 Mon Sep 17 00:00:00 2001 From: Henry Mercer Date: Thu, 30 Jun 2022 17:38:18 +0100 Subject: [PATCH 114/505] Docs: Update guidance to install the _latest_ version of the bundle --- docs/codeql/reusables/beta-note-package-management.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/codeql/reusables/beta-note-package-management.rst b/docs/codeql/reusables/beta-note-package-management.rst index a4fd362a70c..7697c9a47d9 100644 --- a/docs/codeql/reusables/beta-note-package-management.rst +++ b/docs/codeql/reusables/beta-note-package-management.rst @@ -2,4 +2,4 @@ Note - The CodeQL package management functionality, including CodeQL packs, is currently available as a beta release and is subject to change. During the beta release, CodeQL packs are available only using GitHub Packages - the GitHub Container registry. To use this beta functionality, install version 2.6.0 or higher of the CodeQL CLI bundle from: https://github.com/github/codeql-action/releases. \ No newline at end of file + The CodeQL package management functionality, including CodeQL packs, is currently available as a beta release and is subject to change. During the beta release, CodeQL packs are available only using GitHub Packages - the GitHub Container registry. To use this beta functionality, install the latest version of the CodeQL CLI bundle from: https://github.com/github/codeql-action/releases. From dd9306210150b9d63bbc0e4226c8037d2cf58d2f Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Thu, 30 Jun 2022 21:46:55 +0100 Subject: [PATCH 115/505] Kotlin: Mangle names of internal functions to match JVM symbols --- .../src/main/kotlin/KotlinUsesExtractor.kt | 52 +++++++++++-------- java/ql/consistency-queries/visibility.ql | 3 +- .../kotlin/module_mangled_names/User.java | 9 ++++ .../kotlin/module_mangled_names/test.expected | 4 ++ .../kotlin/module_mangled_names/test.py | 3 ++ .../kotlin/module_mangled_names/test.ql | 5 ++ .../kotlin/module_mangled_names/test1.kt | 5 ++ .../kotlin/module_mangled_names/test2.kt | 5 ++ .../kotlin/module_mangled_names/test3.kt | 5 ++ .../internal-public-alias/User.java | 11 ++++ .../internal-public-alias/test.expected | 6 +++ .../internal-public-alias/test.kt | 12 +++++ .../internal-public-alias/test.ql | 5 ++ .../library-tests/methods/methods.expected | 2 +- .../modifiers/modifiers.expected | 2 +- .../properties/properties.expected | 2 +- 16 files changed, 105 insertions(+), 26 deletions(-) create mode 100644 java/ql/integration-tests/posix-only/kotlin/module_mangled_names/User.java create mode 100644 java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test.expected create mode 100644 java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test.py create mode 100644 java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test.ql create mode 100644 java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test1.kt create mode 100644 java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test2.kt create mode 100644 java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test3.kt create mode 100644 java/ql/test/kotlin/library-tests/internal-public-alias/User.java create mode 100644 java/ql/test/kotlin/library-tests/internal-public-alias/test.expected create mode 100644 java/ql/test/kotlin/library-tests/internal-public-alias/test.kt create mode 100644 java/ql/test/kotlin/library-tests/internal-public-alias/test.ql diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt index 530ff47e8ab..63b78e36a19 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt @@ -11,6 +11,7 @@ import org.jetbrains.kotlin.backend.common.lower.parents import org.jetbrains.kotlin.backend.common.lower.parentsWithSelf import org.jetbrains.kotlin.backend.jvm.ir.propertyIfAccessor import org.jetbrains.kotlin.builtins.StandardNames +import org.jetbrains.kotlin.codegen.JvmCodegenUtil import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI import org.jetbrains.kotlin.ir.declarations.* @@ -23,8 +24,10 @@ import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.load.java.sources.JavaSourceElement import org.jetbrains.kotlin.load.java.structure.* +import org.jetbrains.kotlin.load.kotlin.getJvmModuleNameForDeserializedDescriptor import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.name.NameUtils import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.util.OperatorNameConventions @@ -754,11 +757,25 @@ open class KotlinUsesExtractor( data class FunctionNames(val nameInDB: String, val kotlinName: String) + @OptIn(ObsoleteDescriptorBasedAPI::class) + private fun getJvmModuleName(f: IrFunction) = + NameUtils.sanitizeAsJavaIdentifier( + getJvmModuleNameForDeserializedDescriptor(f.descriptor) ?: JvmCodegenUtil.getModuleName(pluginContext.moduleDescriptor) + ) + fun getFunctionShortName(f: IrFunction) : FunctionNames { if (f.origin == IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA || f.isAnonymousFunction) return FunctionNames( OperatorNameConventions.INVOKE.asString(), OperatorNameConventions.INVOKE.asString()) + + fun getSuffixIfInternal() = + if (f.visibility == DescriptorVisibilities.INTERNAL) { + "\$" + getJvmModuleName(f) + } else { + "" + } + (f as? IrSimpleFunction)?.correspondingPropertySymbol?.let { val propName = it.owner.name.asString() val getter = it.owner.getter @@ -774,35 +791,26 @@ open class KotlinUsesExtractor( } } - when (f) { - getter -> { - val defaultFunctionName = JvmAbi.getterName(propName) - val defaultDbName = if (getter.visibility == DescriptorVisibilities.PRIVATE && getter.origin == IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR) { - // In JVM these functions don't exist, instead the backing field is accessed directly - defaultFunctionName + "\$private" - } else { - defaultFunctionName - } - return FunctionNames(getJvmName(getter) ?: defaultDbName, defaultFunctionName) - } - setter -> { - val defaultFunctionName = JvmAbi.setterName(propName) - val defaultDbName = if (setter.visibility == DescriptorVisibilities.PRIVATE && setter.origin == IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR) { - // In JVM these functions don't exist, instead the backing field is accessed directly - defaultFunctionName + "\$private" - } else { - defaultFunctionName - } - return FunctionNames(getJvmName(setter) ?: defaultDbName, defaultFunctionName) - } + val maybeFunctionName = when (f) { + getter -> JvmAbi.getterName(propName) + setter -> JvmAbi.setterName(propName) else -> { logger.error( "Function has a corresponding property, but is neither the getter nor the setter" ) + null } } + maybeFunctionName?.let { defaultFunctionName -> + val suffix = if (f.visibility == DescriptorVisibilities.PRIVATE && f.origin == IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR) { + "\$private" + } else { + getSuffixIfInternal() + } + return FunctionNames(getJvmName(f) ?: "$defaultFunctionName$suffix", defaultFunctionName) + } } - return FunctionNames(getJvmName(f) ?: f.name.asString(), f.name.asString()) + return FunctionNames(getJvmName(f) ?: "${f.name.asString()}${getSuffixIfInternal()}", f.name.asString()) } // This excludes class type parameters that show up in (at least) constructors' typeParameters list. diff --git a/java/ql/consistency-queries/visibility.ql b/java/ql/consistency-queries/visibility.ql index ba90d598236..1b6744cea1d 100644 --- a/java/ql/consistency-queries/visibility.ql +++ b/java/ql/consistency-queries/visibility.ql @@ -18,5 +18,6 @@ where m.getFile().isKotlinSourceFile() and // TODO: This ought to have visibility information not m.getName() = "" and - count(visibility(m)) != 1 + count(visibility(m)) != 1 and + not (count(visibility(m)) = 2 and visibility(m) = "public" and visibility(m) = "internal") // This is a reasonable result, since the JVM symbol is declared public, but Kotlin metadata flags it as internal select m, concat(visibility(m), ", ") diff --git a/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/User.java b/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/User.java new file mode 100644 index 00000000000..12d4b0937da --- /dev/null +++ b/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/User.java @@ -0,0 +1,9 @@ +public class User { + + public static int test(Test1 test1, Test2 test2, Test3 test3) { + + return test1.f$main() + test2.f$mymodule() + test3.f$reservedchars___(); + + } + +} diff --git a/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test.expected b/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test.expected new file mode 100644 index 00000000000..a1fc953a254 --- /dev/null +++ b/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test.expected @@ -0,0 +1,4 @@ +| User.java:3:21:3:24 | test | +| test1.kt:3:12:3:22 | f$main | +| test2.kt:3:12:3:22 | f$mymodule | +| test3.kt:3:12:3:22 | f$reservedchars___ | diff --git a/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test.py b/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test.py new file mode 100644 index 00000000000..0a41ac5b3bf --- /dev/null +++ b/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test.py @@ -0,0 +1,3 @@ +from create_database_utils import * + +run_codeql_database_create(["kotlinc test1.kt", "kotlinc test2.kt -module-name mymodule", "kotlinc test3.kt -module-name reservedchars\\\"${}/", "javac User.java -cp ." ], lang="java") diff --git a/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test.ql b/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test.ql new file mode 100644 index 00000000000..f1355df2e88 --- /dev/null +++ b/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test.ql @@ -0,0 +1,5 @@ +import java + +from Method m +where m.fromSource() +select m diff --git a/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test1.kt b/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test1.kt new file mode 100644 index 00000000000..c14fec0452e --- /dev/null +++ b/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test1.kt @@ -0,0 +1,5 @@ +public class Test1 { + + internal fun f() = 1 + +} diff --git a/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test2.kt b/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test2.kt new file mode 100644 index 00000000000..c37d26c39fc --- /dev/null +++ b/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test2.kt @@ -0,0 +1,5 @@ +public class Test2 { + + internal fun f() = 2 + +} diff --git a/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test3.kt b/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test3.kt new file mode 100644 index 00000000000..5fcdaced80c --- /dev/null +++ b/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test3.kt @@ -0,0 +1,5 @@ +public class Test3 { + + internal fun f() = 3 + +} diff --git a/java/ql/test/kotlin/library-tests/internal-public-alias/User.java b/java/ql/test/kotlin/library-tests/internal-public-alias/User.java new file mode 100644 index 00000000000..d249e1d36f2 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/internal-public-alias/User.java @@ -0,0 +1,11 @@ +public class User { + + public static int test(Test t) { + + t.setInternalVar$main(t.getInternalVal$main()); + + return t.internalFun$main(); + + } + +} diff --git a/java/ql/test/kotlin/library-tests/internal-public-alias/test.expected b/java/ql/test/kotlin/library-tests/internal-public-alias/test.expected new file mode 100644 index 00000000000..77a06cf7310 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/internal-public-alias/test.expected @@ -0,0 +1,6 @@ +| User.java:3:21:3:24 | test | +| test.kt:3:12:3:30 | getInternalVal$main | +| test.kt:6:3:6:36 | getInternalVal | +| test.kt:8:12:8:30 | getInternalVar$main | +| test.kt:8:12:8:30 | setInternalVar$main | +| test.kt:10:12:10:32 | internalFun$main | diff --git a/java/ql/test/kotlin/library-tests/internal-public-alias/test.kt b/java/ql/test/kotlin/library-tests/internal-public-alias/test.kt new file mode 100644 index 00000000000..e79e6d2f907 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/internal-public-alias/test.kt @@ -0,0 +1,12 @@ +public class Test { + + internal val internalVal = 1 + + // Would clash with the internal val's getter without name mangling and provoke a database inconsistency: + fun getInternalVal() = internalVal + + internal var internalVar = 2 + + internal fun internalFun() = 3 + +} diff --git a/java/ql/test/kotlin/library-tests/internal-public-alias/test.ql b/java/ql/test/kotlin/library-tests/internal-public-alias/test.ql new file mode 100644 index 00000000000..f1355df2e88 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/internal-public-alias/test.ql @@ -0,0 +1,5 @@ +import java + +from Method m +where m.fromSource() +select m diff --git a/java/ql/test/kotlin/library-tests/methods/methods.expected b/java/ql/test/kotlin/library-tests/methods/methods.expected index 11fa8762e28..8067838d889 100644 --- a/java/ql/test/kotlin/library-tests/methods/methods.expected +++ b/java/ql/test/kotlin/library-tests/methods/methods.expected @@ -42,7 +42,7 @@ methods | methods.kt:5:1:20:1 | Class | methods.kt:14:12:14:29 | publicFun | publicFun() | public | | | methods.kt:5:1:20:1 | Class | methods.kt:15:15:15:35 | protectedFun | protectedFun() | protected | | | methods.kt:5:1:20:1 | Class | methods.kt:16:13:16:31 | privateFun | privateFun() | private | | -| methods.kt:5:1:20:1 | Class | methods.kt:17:14:17:33 | internalFun | internalFun() | internal | | +| methods.kt:5:1:20:1 | Class | methods.kt:17:14:17:33 | internalFun$main | internalFun$main() | internal | | | methods.kt:5:1:20:1 | Class | methods.kt:18:5:18:36 | noExplicitVisibilityFun | noExplicitVisibilityFun() | public | | | methods.kt:5:1:20:1 | Class | methods.kt:19:12:19:29 | inlineFun | inlineFun() | inline, public | | constructors diff --git a/java/ql/test/kotlin/library-tests/modifiers/modifiers.expected b/java/ql/test/kotlin/library-tests/modifiers/modifiers.expected index 70345c576b0..14b5124b7ae 100644 --- a/java/ql/test/kotlin/library-tests/modifiers/modifiers.expected +++ b/java/ql/test/kotlin/library-tests/modifiers/modifiers.expected @@ -11,7 +11,7 @@ | modifiers.kt:4:5:4:22 | c | Field | final | | modifiers.kt:4:5:4:22 | c | Field | private | | modifiers.kt:4:5:4:22 | c | Property | internal | -| modifiers.kt:4:14:4:22 | getC | Method | internal | +| modifiers.kt:4:14:4:22 | getC$main | Method | internal | | modifiers.kt:5:5:5:34 | d | Field | final | | modifiers.kt:5:5:5:34 | d | Field | private | | modifiers.kt:5:5:5:34 | d | Property | public | diff --git a/java/ql/test/kotlin/library-tests/properties/properties.expected b/java/ql/test/kotlin/library-tests/properties/properties.expected index 05870c7b6e1..7bbe706923c 100644 --- a/java/ql/test/kotlin/library-tests/properties/properties.expected +++ b/java/ql/test/kotlin/library-tests/properties/properties.expected @@ -45,7 +45,7 @@ fieldDeclarations | properties.kt:35:5:35:32 | privateProp | properties.kt:35:13:35:32 | getPrivateProp$private | file://:0:0:0:0 | | properties.kt:35:5:35:32 | privateProp | private | | properties.kt:36:5:36:36 | protectedProp | properties.kt:36:15:36:36 | getProtectedProp | file://:0:0:0:0 | | properties.kt:36:5:36:36 | protectedProp | protected | | properties.kt:37:5:37:30 | publicProp | properties.kt:37:12:37:30 | getPublicProp | file://:0:0:0:0 | | properties.kt:37:5:37:30 | publicProp | public | -| properties.kt:38:5:38:34 | internalProp | properties.kt:38:14:38:34 | getInternalProp | file://:0:0:0:0 | | properties.kt:38:5:38:34 | internalProp | internal | +| properties.kt:38:5:38:34 | internalProp | properties.kt:38:14:38:34 | getInternalProp$main | file://:0:0:0:0 | | properties.kt:38:5:38:34 | internalProp | internal | | properties.kt:67:1:67:23 | constVal | properties.kt:67:7:67:23 | getConstVal | file://:0:0:0:0 | | properties.kt:67:1:67:23 | constVal | public | | properties.kt:70:5:70:16 | prop | properties.kt:70:5:70:16 | getProp | file://:0:0:0:0 | | properties.kt:70:5:70:16 | prop | public | | properties.kt:78:1:79:13 | x | properties.kt:79:5:79:13 | getX | file://:0:0:0:0 | | file://:0:0:0:0 | | public | From 14aef792e0b7db92b303eaf57cfbc9132c0f1e6b Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Fri, 1 Jul 2022 10:35:17 +0100 Subject: [PATCH 116/505] Accept test changes --- .../java-kotlin-collection-type-generic-methods/test.expected | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/ql/test/kotlin/library-tests/java-kotlin-collection-type-generic-methods/test.expected b/java/ql/test/kotlin/library-tests/java-kotlin-collection-type-generic-methods/test.expected index 417b8a22399..7995948aa78 100644 --- a/java/ql/test/kotlin/library-tests/java-kotlin-collection-type-generic-methods/test.expected +++ b/java/ql/test/kotlin/library-tests/java-kotlin-collection-type-generic-methods/test.expected @@ -54,7 +54,7 @@ methodWithDuplicate | AbstractList | set | int | | AbstractList | subList | int | | AbstractList | subListRangeCheck | int | -| AbstractMap | containsEntry | Entry | +| AbstractMap | containsEntry$kotlin_stdlib | Entry | | AbstractMap | containsKey | Object | | AbstractMap | containsValue | Object | | AbstractMap | equals | Object | @@ -79,7 +79,7 @@ methodWithDuplicate | AbstractMap | put | V | | AbstractMap | putAll | Map | | AbstractMap | remove | Object | -| AbstractMap | containsEntry | Entry | +| AbstractMap | containsEntry$kotlin_stdlib | Entry | | AbstractMap | containsKey | Object | | AbstractMap | containsValue | Object | | AbstractMap | equals | Object | From e4636be8dbb91fb01b5607c5600c5b906009f521 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 1 Jul 2022 11:07:18 +0100 Subject: [PATCH 117/505] C++: Add 'nomagic' to the charpred of 'VariableAccessInInitializer'. --- cpp/ql/src/Likely Bugs/UseInOwnInitializer.ql | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp/ql/src/Likely Bugs/UseInOwnInitializer.ql b/cpp/ql/src/Likely Bugs/UseInOwnInitializer.ql index 6bb411b7844..054fdccbb99 100644 --- a/cpp/ql/src/Likely Bugs/UseInOwnInitializer.ql +++ b/cpp/ql/src/Likely Bugs/UseInOwnInitializer.ql @@ -15,6 +15,7 @@ class VariableAccessInInitializer extends VariableAccess { Variable var; Initializer init; + pragma[nomagic] VariableAccessInInitializer() { init.getDeclaration() = var and init.getExpr().getAChild*() = this From 901e0663557571604239ec44e44ddefaacf3dc1f Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Fri, 1 Jul 2022 13:39:28 +0200 Subject: [PATCH 118/505] Swift: locally run integration tests Minimal recreations of internal `integration-tests-runner.py` and `create_database_utils.py` are provided to be able to run the integration tests on the codeql repository with a released codeql CLI. For the moment we skip the database checks by default, as we are still producing inconsistent results. --- .github/workflows/swift-integration-tests.yml | 32 ++++++++ swift/integration-tests/.gitignore | 1 + .../create_database_utils.py | 26 ++++++ .../cross-references/Functions.expected | 4 - .../frontend-invocations/A.swift | 0 .../frontend-invocations/B.swift | 0 .../frontend-invocations/C.swift | 0 .../frontend-invocations/D.swift | 0 .../frontend-invocations/E.swift | 0 .../frontend-invocations/Esup.swift | 0 .../frontend-invocations/Files.expected | 0 .../frontend-invocations/Files.ql | 0 .../frontend-invocations/Makefile | 0 .../frontend-invocations/test.py | 0 .../cross-references/Classes.expected | 0 .../cross-references/Classes.ql | 0 .../cross-references/Constructors.expected | 0 .../cross-references/Constructors.ql | 0 .../cross-references/Destructors.expected | 0 .../cross-references/Destructors.ql | 0 .../cross-references/Enums.expected | 0 .../cross-references/Enums.ql | 0 .../cross-references/Functions.expected | 4 + .../cross-references/Functions.ql | 0 .../cross-references/Operators.expected | 0 .../cross-references/Operators.ql | 0 .../cross-references/Package.swift | 0 .../cross-references/Protocols.expected | 0 .../cross-references/Protocols.ql | 0 .../Sources/cross-references/lib.swift | 1 - .../Sources/cross-references/main.swift | 1 - .../cross-references/Structs.expected | 0 .../cross-references/Structs.ql | 0 .../cross-references/VarDecls.expected | 0 .../cross-references/VarDecls.ql | 0 .../{ => posix-only}/cross-references/test.py | 0 .../hello-world/Package.swift | 0 .../Sources/hello-world/hello_world.swift | 0 .../hello-world/test.expected | 0 .../{ => posix-only}/hello-world/test.py | 0 .../{ => posix-only}/hello-world/test.ql | 0 .../partial-modules/A/Package.swift | 0 .../partial-modules/A/Sources/A/A.swift | 0 .../partial-modules/A/Sources/A/Asup.swift | 0 .../partial-modules/B/Package.swift | 0 .../partial-modules/B/Sources/B/B.swift | 0 .../partial-modules/B/Sources/B/Bsup.swift | 0 .../partial-modules/Package.swift | 0 .../partial-modules/partial_modules.swift | 0 .../partial-modules/Unknown.expected | 0 .../partial-modules/Unknown.ql | 0 .../{ => posix-only}/partial-modules/test.py | 0 swift/integration-tests/runner.py | 80 +++++++++++++++++++ 53 files changed, 143 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/swift-integration-tests.yml create mode 100644 swift/integration-tests/create_database_utils.py delete mode 100644 swift/integration-tests/cross-references/Functions.expected rename swift/integration-tests/{ => osx-only}/frontend-invocations/A.swift (100%) rename swift/integration-tests/{ => osx-only}/frontend-invocations/B.swift (100%) rename swift/integration-tests/{ => osx-only}/frontend-invocations/C.swift (100%) rename swift/integration-tests/{ => osx-only}/frontend-invocations/D.swift (100%) rename swift/integration-tests/{ => osx-only}/frontend-invocations/E.swift (100%) rename swift/integration-tests/{ => osx-only}/frontend-invocations/Esup.swift (100%) rename swift/integration-tests/{ => osx-only}/frontend-invocations/Files.expected (100%) rename swift/integration-tests/{ => osx-only}/frontend-invocations/Files.ql (100%) rename swift/integration-tests/{ => osx-only}/frontend-invocations/Makefile (100%) rename swift/integration-tests/{ => osx-only}/frontend-invocations/test.py (100%) rename swift/integration-tests/{ => posix-only}/cross-references/Classes.expected (100%) rename swift/integration-tests/{ => posix-only}/cross-references/Classes.ql (100%) rename swift/integration-tests/{ => posix-only}/cross-references/Constructors.expected (100%) rename swift/integration-tests/{ => posix-only}/cross-references/Constructors.ql (100%) rename swift/integration-tests/{ => posix-only}/cross-references/Destructors.expected (100%) rename swift/integration-tests/{ => posix-only}/cross-references/Destructors.ql (100%) rename swift/integration-tests/{ => posix-only}/cross-references/Enums.expected (100%) rename swift/integration-tests/{ => posix-only}/cross-references/Enums.ql (100%) create mode 100644 swift/integration-tests/posix-only/cross-references/Functions.expected rename swift/integration-tests/{ => posix-only}/cross-references/Functions.ql (100%) rename swift/integration-tests/{ => posix-only}/cross-references/Operators.expected (100%) rename swift/integration-tests/{ => posix-only}/cross-references/Operators.ql (100%) rename swift/integration-tests/{ => posix-only}/cross-references/Package.swift (100%) rename swift/integration-tests/{ => posix-only}/cross-references/Protocols.expected (100%) rename swift/integration-tests/{ => posix-only}/cross-references/Protocols.ql (100%) rename swift/integration-tests/{ => posix-only}/cross-references/Sources/cross-references/lib.swift (99%) rename swift/integration-tests/{ => posix-only}/cross-references/Sources/cross-references/main.swift (98%) rename swift/integration-tests/{ => posix-only}/cross-references/Structs.expected (100%) rename swift/integration-tests/{ => posix-only}/cross-references/Structs.ql (100%) rename swift/integration-tests/{ => posix-only}/cross-references/VarDecls.expected (100%) rename swift/integration-tests/{ => posix-only}/cross-references/VarDecls.ql (100%) rename swift/integration-tests/{ => posix-only}/cross-references/test.py (100%) rename swift/integration-tests/{ => posix-only}/hello-world/Package.swift (100%) rename swift/integration-tests/{ => posix-only}/hello-world/Sources/hello-world/hello_world.swift (100%) rename swift/integration-tests/{ => posix-only}/hello-world/test.expected (100%) rename swift/integration-tests/{ => posix-only}/hello-world/test.py (100%) rename swift/integration-tests/{ => posix-only}/hello-world/test.ql (100%) rename swift/integration-tests/{ => posix-only}/partial-modules/A/Package.swift (100%) rename swift/integration-tests/{ => posix-only}/partial-modules/A/Sources/A/A.swift (100%) rename swift/integration-tests/{ => posix-only}/partial-modules/A/Sources/A/Asup.swift (100%) rename swift/integration-tests/{ => posix-only}/partial-modules/B/Package.swift (100%) rename swift/integration-tests/{ => posix-only}/partial-modules/B/Sources/B/B.swift (100%) rename swift/integration-tests/{ => posix-only}/partial-modules/B/Sources/B/Bsup.swift (100%) rename swift/integration-tests/{ => posix-only}/partial-modules/Package.swift (100%) rename swift/integration-tests/{ => posix-only}/partial-modules/Sources/partial-modules/partial_modules.swift (100%) rename swift/integration-tests/{ => posix-only}/partial-modules/Unknown.expected (100%) rename swift/integration-tests/{ => posix-only}/partial-modules/Unknown.ql (100%) rename swift/integration-tests/{ => posix-only}/partial-modules/test.py (100%) create mode 100755 swift/integration-tests/runner.py diff --git a/.github/workflows/swift-integration-tests.yml b/.github/workflows/swift-integration-tests.yml new file mode 100644 index 00000000000..a9028d6c89a --- /dev/null +++ b/.github/workflows/swift-integration-tests.yml @@ -0,0 +1,32 @@ +name: "Swift: Run Integration Tests" + +on: + pull_request: + paths: + - "swift/**" + - .github/workflows/swift-integration-tests.yml + - codeql-workspace.yml + branches: + - main +defaults: + run: + working-directory: swift + +jobs: + integration-tests: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os : [ubuntu-20.04, macos-latest] + steps: + - uses: actions/checkout@v3 + - uses: ./.github/actions/fetch-codeql + - uses: bazelbuild/setup-bazelisk@v2 + - uses: actions/setup-python@v3 + - name: Build Swift extractor + run: | + bazel run //swift:create-extractor-pack + - name: Run integration tests + run: | + python integration-tests/runner.py diff --git a/swift/integration-tests/.gitignore b/swift/integration-tests/.gitignore index 8e66c817556..9ea4244ad91 100644 --- a/swift/integration-tests/.gitignore +++ b/swift/integration-tests/.gitignore @@ -6,3 +6,4 @@ xcuserdata/ DerivedData/ .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata *.actual +db diff --git a/swift/integration-tests/create_database_utils.py b/swift/integration-tests/create_database_utils.py new file mode 100644 index 00000000000..3f2d11a39f7 --- /dev/null +++ b/swift/integration-tests/create_database_utils.py @@ -0,0 +1,26 @@ +""" +recreation of internal `create_database_utils.py` to run the tests locally, with minimal +and swift-specialized functionality +""" +import subprocess +import pathlib +import sys + + +def run_codeql_database_create(cmds, lang, keep_trap=True): + assert lang == 'swift' + codeql_root = pathlib.Path(__file__).parents[2] + cmd = [ + "codeql", "database", "create", + "-s", ".", "-l", "swift", "--internal-use-lua-tracing", f"--search-path={codeql_root}", + ] + if keep_trap: + cmd.append("--keep-trap") + for c in cmds: + cmd += ["-c", c] + cmd.append("db") + res = subprocess.run(cmd) + if res.returncode: + print("FAILED", file=sys.stderr) + print(" ", *cmd, file=sys.stderr) + sys.exit(res.returncode) diff --git a/swift/integration-tests/cross-references/Functions.expected b/swift/integration-tests/cross-references/Functions.expected deleted file mode 100644 index 76335112240..00000000000 --- a/swift/integration-tests/cross-references/Functions.expected +++ /dev/null @@ -1,4 +0,0 @@ -| Sources/cross-references/lib.swift:1:1:1:11 | f | -| Sources/cross-references/lib.swift:17:8:19:1 | ~ | -| Sources/cross-references/lib.swift:22:9:24:1 | ~ | -| Sources/cross-references/lib.swift:27:1:29:1 | ~ | diff --git a/swift/integration-tests/frontend-invocations/A.swift b/swift/integration-tests/osx-only/frontend-invocations/A.swift similarity index 100% rename from swift/integration-tests/frontend-invocations/A.swift rename to swift/integration-tests/osx-only/frontend-invocations/A.swift diff --git a/swift/integration-tests/frontend-invocations/B.swift b/swift/integration-tests/osx-only/frontend-invocations/B.swift similarity index 100% rename from swift/integration-tests/frontend-invocations/B.swift rename to swift/integration-tests/osx-only/frontend-invocations/B.swift diff --git a/swift/integration-tests/frontend-invocations/C.swift b/swift/integration-tests/osx-only/frontend-invocations/C.swift similarity index 100% rename from swift/integration-tests/frontend-invocations/C.swift rename to swift/integration-tests/osx-only/frontend-invocations/C.swift diff --git a/swift/integration-tests/frontend-invocations/D.swift b/swift/integration-tests/osx-only/frontend-invocations/D.swift similarity index 100% rename from swift/integration-tests/frontend-invocations/D.swift rename to swift/integration-tests/osx-only/frontend-invocations/D.swift diff --git a/swift/integration-tests/frontend-invocations/E.swift b/swift/integration-tests/osx-only/frontend-invocations/E.swift similarity index 100% rename from swift/integration-tests/frontend-invocations/E.swift rename to swift/integration-tests/osx-only/frontend-invocations/E.swift diff --git a/swift/integration-tests/frontend-invocations/Esup.swift b/swift/integration-tests/osx-only/frontend-invocations/Esup.swift similarity index 100% rename from swift/integration-tests/frontend-invocations/Esup.swift rename to swift/integration-tests/osx-only/frontend-invocations/Esup.swift diff --git a/swift/integration-tests/frontend-invocations/Files.expected b/swift/integration-tests/osx-only/frontend-invocations/Files.expected similarity index 100% rename from swift/integration-tests/frontend-invocations/Files.expected rename to swift/integration-tests/osx-only/frontend-invocations/Files.expected diff --git a/swift/integration-tests/frontend-invocations/Files.ql b/swift/integration-tests/osx-only/frontend-invocations/Files.ql similarity index 100% rename from swift/integration-tests/frontend-invocations/Files.ql rename to swift/integration-tests/osx-only/frontend-invocations/Files.ql diff --git a/swift/integration-tests/frontend-invocations/Makefile b/swift/integration-tests/osx-only/frontend-invocations/Makefile similarity index 100% rename from swift/integration-tests/frontend-invocations/Makefile rename to swift/integration-tests/osx-only/frontend-invocations/Makefile diff --git a/swift/integration-tests/frontend-invocations/test.py b/swift/integration-tests/osx-only/frontend-invocations/test.py similarity index 100% rename from swift/integration-tests/frontend-invocations/test.py rename to swift/integration-tests/osx-only/frontend-invocations/test.py diff --git a/swift/integration-tests/cross-references/Classes.expected b/swift/integration-tests/posix-only/cross-references/Classes.expected similarity index 100% rename from swift/integration-tests/cross-references/Classes.expected rename to swift/integration-tests/posix-only/cross-references/Classes.expected diff --git a/swift/integration-tests/cross-references/Classes.ql b/swift/integration-tests/posix-only/cross-references/Classes.ql similarity index 100% rename from swift/integration-tests/cross-references/Classes.ql rename to swift/integration-tests/posix-only/cross-references/Classes.ql diff --git a/swift/integration-tests/cross-references/Constructors.expected b/swift/integration-tests/posix-only/cross-references/Constructors.expected similarity index 100% rename from swift/integration-tests/cross-references/Constructors.expected rename to swift/integration-tests/posix-only/cross-references/Constructors.expected diff --git a/swift/integration-tests/cross-references/Constructors.ql b/swift/integration-tests/posix-only/cross-references/Constructors.ql similarity index 100% rename from swift/integration-tests/cross-references/Constructors.ql rename to swift/integration-tests/posix-only/cross-references/Constructors.ql diff --git a/swift/integration-tests/cross-references/Destructors.expected b/swift/integration-tests/posix-only/cross-references/Destructors.expected similarity index 100% rename from swift/integration-tests/cross-references/Destructors.expected rename to swift/integration-tests/posix-only/cross-references/Destructors.expected diff --git a/swift/integration-tests/cross-references/Destructors.ql b/swift/integration-tests/posix-only/cross-references/Destructors.ql similarity index 100% rename from swift/integration-tests/cross-references/Destructors.ql rename to swift/integration-tests/posix-only/cross-references/Destructors.ql diff --git a/swift/integration-tests/cross-references/Enums.expected b/swift/integration-tests/posix-only/cross-references/Enums.expected similarity index 100% rename from swift/integration-tests/cross-references/Enums.expected rename to swift/integration-tests/posix-only/cross-references/Enums.expected diff --git a/swift/integration-tests/cross-references/Enums.ql b/swift/integration-tests/posix-only/cross-references/Enums.ql similarity index 100% rename from swift/integration-tests/cross-references/Enums.ql rename to swift/integration-tests/posix-only/cross-references/Enums.ql diff --git a/swift/integration-tests/posix-only/cross-references/Functions.expected b/swift/integration-tests/posix-only/cross-references/Functions.expected new file mode 100644 index 00000000000..2ecf78a6be4 --- /dev/null +++ b/swift/integration-tests/posix-only/cross-references/Functions.expected @@ -0,0 +1,4 @@ +| Sources/cross-references/lib.swift:1:1:1:11 | f() | +| Sources/cross-references/lib.swift:17:8:19:1 | ~(_:) | +| Sources/cross-references/lib.swift:22:9:24:1 | ~(_:) | +| Sources/cross-references/lib.swift:27:1:29:1 | ~(_:_:) | diff --git a/swift/integration-tests/cross-references/Functions.ql b/swift/integration-tests/posix-only/cross-references/Functions.ql similarity index 100% rename from swift/integration-tests/cross-references/Functions.ql rename to swift/integration-tests/posix-only/cross-references/Functions.ql diff --git a/swift/integration-tests/cross-references/Operators.expected b/swift/integration-tests/posix-only/cross-references/Operators.expected similarity index 100% rename from swift/integration-tests/cross-references/Operators.expected rename to swift/integration-tests/posix-only/cross-references/Operators.expected diff --git a/swift/integration-tests/cross-references/Operators.ql b/swift/integration-tests/posix-only/cross-references/Operators.ql similarity index 100% rename from swift/integration-tests/cross-references/Operators.ql rename to swift/integration-tests/posix-only/cross-references/Operators.ql diff --git a/swift/integration-tests/cross-references/Package.swift b/swift/integration-tests/posix-only/cross-references/Package.swift similarity index 100% rename from swift/integration-tests/cross-references/Package.swift rename to swift/integration-tests/posix-only/cross-references/Package.swift diff --git a/swift/integration-tests/cross-references/Protocols.expected b/swift/integration-tests/posix-only/cross-references/Protocols.expected similarity index 100% rename from swift/integration-tests/cross-references/Protocols.expected rename to swift/integration-tests/posix-only/cross-references/Protocols.expected diff --git a/swift/integration-tests/cross-references/Protocols.ql b/swift/integration-tests/posix-only/cross-references/Protocols.ql similarity index 100% rename from swift/integration-tests/cross-references/Protocols.ql rename to swift/integration-tests/posix-only/cross-references/Protocols.ql diff --git a/swift/integration-tests/cross-references/Sources/cross-references/lib.swift b/swift/integration-tests/posix-only/cross-references/Sources/cross-references/lib.swift similarity index 99% rename from swift/integration-tests/cross-references/Sources/cross-references/lib.swift rename to swift/integration-tests/posix-only/cross-references/Sources/cross-references/lib.swift index 39acad28b14..1155935d326 100644 --- a/swift/integration-tests/cross-references/Sources/cross-references/lib.swift +++ b/swift/integration-tests/posix-only/cross-references/Sources/cross-references/lib.swift @@ -27,4 +27,3 @@ infix operator ~ func ~(lhs: Int, rhs: Int) -> Int { return lhs } - diff --git a/swift/integration-tests/cross-references/Sources/cross-references/main.swift b/swift/integration-tests/posix-only/cross-references/Sources/cross-references/main.swift similarity index 98% rename from swift/integration-tests/cross-references/Sources/cross-references/main.swift rename to swift/integration-tests/posix-only/cross-references/Sources/cross-references/main.swift index b4143493c41..474d885af58 100644 --- a/swift/integration-tests/cross-references/Sources/cross-references/main.swift +++ b/swift/integration-tests/posix-only/cross-references/Sources/cross-references/main.swift @@ -15,4 +15,3 @@ struct s : P {} 42~ 15 ~ 42 - diff --git a/swift/integration-tests/cross-references/Structs.expected b/swift/integration-tests/posix-only/cross-references/Structs.expected similarity index 100% rename from swift/integration-tests/cross-references/Structs.expected rename to swift/integration-tests/posix-only/cross-references/Structs.expected diff --git a/swift/integration-tests/cross-references/Structs.ql b/swift/integration-tests/posix-only/cross-references/Structs.ql similarity index 100% rename from swift/integration-tests/cross-references/Structs.ql rename to swift/integration-tests/posix-only/cross-references/Structs.ql diff --git a/swift/integration-tests/cross-references/VarDecls.expected b/swift/integration-tests/posix-only/cross-references/VarDecls.expected similarity index 100% rename from swift/integration-tests/cross-references/VarDecls.expected rename to swift/integration-tests/posix-only/cross-references/VarDecls.expected diff --git a/swift/integration-tests/cross-references/VarDecls.ql b/swift/integration-tests/posix-only/cross-references/VarDecls.ql similarity index 100% rename from swift/integration-tests/cross-references/VarDecls.ql rename to swift/integration-tests/posix-only/cross-references/VarDecls.ql diff --git a/swift/integration-tests/cross-references/test.py b/swift/integration-tests/posix-only/cross-references/test.py similarity index 100% rename from swift/integration-tests/cross-references/test.py rename to swift/integration-tests/posix-only/cross-references/test.py diff --git a/swift/integration-tests/hello-world/Package.swift b/swift/integration-tests/posix-only/hello-world/Package.swift similarity index 100% rename from swift/integration-tests/hello-world/Package.swift rename to swift/integration-tests/posix-only/hello-world/Package.swift diff --git a/swift/integration-tests/hello-world/Sources/hello-world/hello_world.swift b/swift/integration-tests/posix-only/hello-world/Sources/hello-world/hello_world.swift similarity index 100% rename from swift/integration-tests/hello-world/Sources/hello-world/hello_world.swift rename to swift/integration-tests/posix-only/hello-world/Sources/hello-world/hello_world.swift diff --git a/swift/integration-tests/hello-world/test.expected b/swift/integration-tests/posix-only/hello-world/test.expected similarity index 100% rename from swift/integration-tests/hello-world/test.expected rename to swift/integration-tests/posix-only/hello-world/test.expected diff --git a/swift/integration-tests/hello-world/test.py b/swift/integration-tests/posix-only/hello-world/test.py similarity index 100% rename from swift/integration-tests/hello-world/test.py rename to swift/integration-tests/posix-only/hello-world/test.py diff --git a/swift/integration-tests/hello-world/test.ql b/swift/integration-tests/posix-only/hello-world/test.ql similarity index 100% rename from swift/integration-tests/hello-world/test.ql rename to swift/integration-tests/posix-only/hello-world/test.ql diff --git a/swift/integration-tests/partial-modules/A/Package.swift b/swift/integration-tests/posix-only/partial-modules/A/Package.swift similarity index 100% rename from swift/integration-tests/partial-modules/A/Package.swift rename to swift/integration-tests/posix-only/partial-modules/A/Package.swift diff --git a/swift/integration-tests/partial-modules/A/Sources/A/A.swift b/swift/integration-tests/posix-only/partial-modules/A/Sources/A/A.swift similarity index 100% rename from swift/integration-tests/partial-modules/A/Sources/A/A.swift rename to swift/integration-tests/posix-only/partial-modules/A/Sources/A/A.swift diff --git a/swift/integration-tests/partial-modules/A/Sources/A/Asup.swift b/swift/integration-tests/posix-only/partial-modules/A/Sources/A/Asup.swift similarity index 100% rename from swift/integration-tests/partial-modules/A/Sources/A/Asup.swift rename to swift/integration-tests/posix-only/partial-modules/A/Sources/A/Asup.swift diff --git a/swift/integration-tests/partial-modules/B/Package.swift b/swift/integration-tests/posix-only/partial-modules/B/Package.swift similarity index 100% rename from swift/integration-tests/partial-modules/B/Package.swift rename to swift/integration-tests/posix-only/partial-modules/B/Package.swift diff --git a/swift/integration-tests/partial-modules/B/Sources/B/B.swift b/swift/integration-tests/posix-only/partial-modules/B/Sources/B/B.swift similarity index 100% rename from swift/integration-tests/partial-modules/B/Sources/B/B.swift rename to swift/integration-tests/posix-only/partial-modules/B/Sources/B/B.swift diff --git a/swift/integration-tests/partial-modules/B/Sources/B/Bsup.swift b/swift/integration-tests/posix-only/partial-modules/B/Sources/B/Bsup.swift similarity index 100% rename from swift/integration-tests/partial-modules/B/Sources/B/Bsup.swift rename to swift/integration-tests/posix-only/partial-modules/B/Sources/B/Bsup.swift diff --git a/swift/integration-tests/partial-modules/Package.swift b/swift/integration-tests/posix-only/partial-modules/Package.swift similarity index 100% rename from swift/integration-tests/partial-modules/Package.swift rename to swift/integration-tests/posix-only/partial-modules/Package.swift diff --git a/swift/integration-tests/partial-modules/Sources/partial-modules/partial_modules.swift b/swift/integration-tests/posix-only/partial-modules/Sources/partial-modules/partial_modules.swift similarity index 100% rename from swift/integration-tests/partial-modules/Sources/partial-modules/partial_modules.swift rename to swift/integration-tests/posix-only/partial-modules/Sources/partial-modules/partial_modules.swift diff --git a/swift/integration-tests/partial-modules/Unknown.expected b/swift/integration-tests/posix-only/partial-modules/Unknown.expected similarity index 100% rename from swift/integration-tests/partial-modules/Unknown.expected rename to swift/integration-tests/posix-only/partial-modules/Unknown.expected diff --git a/swift/integration-tests/partial-modules/Unknown.ql b/swift/integration-tests/posix-only/partial-modules/Unknown.ql similarity index 100% rename from swift/integration-tests/partial-modules/Unknown.ql rename to swift/integration-tests/posix-only/partial-modules/Unknown.ql diff --git a/swift/integration-tests/partial-modules/test.py b/swift/integration-tests/posix-only/partial-modules/test.py similarity index 100% rename from swift/integration-tests/partial-modules/test.py rename to swift/integration-tests/posix-only/partial-modules/test.py diff --git a/swift/integration-tests/runner.py b/swift/integration-tests/runner.py new file mode 100755 index 00000000000..b9e39325fd9 --- /dev/null +++ b/swift/integration-tests/runner.py @@ -0,0 +1,80 @@ +#!/bin/env python3 +""" +recreation of internal `integration-tests-runner.py` to run the tests locally, with minimal +and swift-specialized functionality. + +This runner requires: +* a codeql CLI binary in PATH +* `bazel run //swift:create_extractor_pack` to have been run +""" + +import pathlib +import os +import sys +import subprocess +import argparse +import shutil +import platform + +this_dir = pathlib.Path(__file__).parent + + +def options(): + p = argparse.ArgumentParser() + p.add_argument("--test-dir", "-d", type=pathlib.Path, action="append") + #FIXME: the following should be the default + p.add_argument("--check-databases", action="store_true") + p.add_argument("--learn", action="store_true") + p.add_argument("--threads", "-j", type=int, default=0) + return p.parse_args() + + +def execute_test(path): + shutil.rmtree(path.parent / "db", ignore_errors=True) + return subprocess.run([sys.executable, "-u", path.name], cwd=path.parent).returncode == 0 + +def skipped(test): + return platform.system() != "Darwin" and "osx-only" in test.parts + + +def main(opts): + test_dirs = opts.test_dir or [this_dir] + tests = [t for d in test_dirs for t in d.rglob("test.py") if not skipped(t)] + + if not tests: + print("No tests found", file=sys.stderr) + return False + + os.environ["PYTHONPATH"] = str(this_dir) + failed_db_creation = [] + succesful_db_creation = [] + for t in tests: + (succesful_db_creation if execute_test(t) else failed_db_creation).append(t) + + if succesful_db_creation: + codeql_root = this_dir.parents[1] + cmd = [ + "codeql", "test", "run", + f"--search-path={codeql_root}", + "--keep-databases", + "--dataset=db/db-swift", + f"--threads={opts.threads}", + ] + if opts.check_databases: + cmd.append("--check-databases") + if opts.learn: + cmd.append("--learn") + cmd.extend(str(t.parent) for t in succesful_db_creation) + ql_test_success = subprocess.run(cmd).returncode == 0 + + if failed_db_creation: + print("Database creation failed:", file=sys.stderr) + for t in failed_db_creation: + print(" ", t.parent, file=sys.stderr) + return False + + return ql_test_success + + +if __name__ == "__main__": + sys.exit(0 if main(options()) else 1) From 24da81fdb068e2ea0cc43b27686d6281fe042eb1 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Fri, 1 Jul 2022 14:56:14 +0200 Subject: [PATCH 119/505] Swift: disable integration tests on macOS for now Also, add swift workflow to code owned by the C team --- .github/workflows/swift-integration-tests.yml | 4 +++- CODEOWNERS | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/swift-integration-tests.yml b/.github/workflows/swift-integration-tests.yml index a9028d6c89a..591ea2b12f7 100644 --- a/.github/workflows/swift-integration-tests.yml +++ b/.github/workflows/swift-integration-tests.yml @@ -18,7 +18,9 @@ jobs: strategy: fail-fast: false matrix: - os : [ubuntu-20.04, macos-latest] + os: + - ubuntu-20.04 +# - macos-latest TODO steps: - uses: actions/checkout@v3 - uses: ./.github/actions/fetch-codeql diff --git a/CODEOWNERS b/CODEOWNERS index da71d1ec5d8..1754d58af63 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -42,3 +42,4 @@ WORKSPACE.bazel @github/codeql-ci-reviewers /.github/workflows/js-ml-tests.yml @github/codeql-ml-powered-queries-reviewers /.github/workflows/ql-for-ql-* @github/codeql-ql-for-ql-reviewers /.github/workflows/ruby-* @github/codeql-ruby +/.github/workflows/swift-* @github/codeql-c From 7a7440a1155ed927d8ac242d2acf3f9494ace486 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Wed, 29 Jun 2022 10:52:18 +0200 Subject: [PATCH 120/505] Swift: move `createEntry` to `SwiftDispatcher` --- .../collections4/map/AbstractHashedMap.java | 2 +- .../collections4/map/AbstractLinkedMap.java | 2 +- swift/extractor/infra/SwiftDispatcher.h | 6 +++ swift/extractor/visitors/TypeVisitor.cpp | 42 +++++++++---------- swift/extractor/visitors/TypeVisitor.h | 4 +- 5 files changed, 31 insertions(+), 25 deletions(-) diff --git a/java/ql/test/stubs/apache-commons-collections4-4.4/org/apache/commons/collections4/map/AbstractHashedMap.java b/java/ql/test/stubs/apache-commons-collections4-4.4/org/apache/commons/collections4/map/AbstractHashedMap.java index f79236e4f10..35d673df2c8 100644 --- a/java/ql/test/stubs/apache-commons-collections4-4.4/org/apache/commons/collections4/map/AbstractHashedMap.java +++ b/java/ql/test/stubs/apache-commons-collections4-4.4/org/apache/commons/collections4/map/AbstractHashedMap.java @@ -30,7 +30,7 @@ public class AbstractHashedMap extends AbstractMap implements Iterab protected AbstractHashedMap(int p0){} protected AbstractHashedMap(int p0, float p1){} protected AbstractHashedMap(int p0, float p1, int p2){} - protected AbstractHashedMap.HashEntry createEntry(AbstractHashedMap.HashEntry p0, int p1, K p2, V p3){ return null; } + protected AbstractHashedMap.HashEntry createTypeEntry(AbstractHashedMap.HashEntry p0, int p1, K p2, V p3){ return null; } protected AbstractHashedMap.HashEntry entryNext(AbstractHashedMap.HashEntry p0){ return null; } protected AbstractHashedMap.HashEntry getEntry(Object p0){ return null; } protected AbstractHashedMap clone(){ return null; } diff --git a/java/ql/test/stubs/apache-commons-collections4-4.4/org/apache/commons/collections4/map/AbstractLinkedMap.java b/java/ql/test/stubs/apache-commons-collections4-4.4/org/apache/commons/collections4/map/AbstractLinkedMap.java index b96404dd5b9..6d349ee1130 100644 --- a/java/ql/test/stubs/apache-commons-collections4-4.4/org/apache/commons/collections4/map/AbstractLinkedMap.java +++ b/java/ql/test/stubs/apache-commons-collections4-4.4/org/apache/commons/collections4/map/AbstractLinkedMap.java @@ -16,7 +16,7 @@ abstract public class AbstractLinkedMap extends AbstractHashedMap im protected AbstractLinkedMap(int p0){} protected AbstractLinkedMap(int p0, float p1){} protected AbstractLinkedMap(int p0, float p1, int p2){} - protected AbstractLinkedMap.LinkEntry createEntry(AbstractHashedMap.HashEntry p0, int p1, K p2, V p3){ return null; } + protected AbstractLinkedMap.LinkEntry createTypeEntry(AbstractHashedMap.HashEntry p0, int p1, K p2, V p3){ return null; } protected AbstractLinkedMap.LinkEntry entryAfter(AbstractLinkedMap.LinkEntry p0){ return null; } protected AbstractLinkedMap.LinkEntry entryBefore(AbstractLinkedMap.LinkEntry p0){ return null; } protected AbstractLinkedMap.LinkEntry getEntry(Object p0){ return null; } diff --git a/swift/extractor/infra/SwiftDispatcher.h b/swift/extractor/infra/SwiftDispatcher.h index e7aaa5af612..525cb74330a 100644 --- a/swift/extractor/infra/SwiftDispatcher.h +++ b/swift/extractor/infra/SwiftDispatcher.h @@ -112,6 +112,12 @@ class SwiftDispatcher { return assignNewLabel(&e, std::forward(args)...); } + // convenience methods for structured C++ creation + template >* = nullptr> + auto createEntry(const E& e, Args&&... args) { + return TrapClassOf{assignNewLabel(&e, std::forward(args)...)}; + } + template TrapLabel createLabel() { auto ret = arena.allocateLabel(); diff --git a/swift/extractor/visitors/TypeVisitor.cpp b/swift/extractor/visitors/TypeVisitor.cpp index 4514692421e..4b6733312d3 100644 --- a/swift/extractor/visitors/TypeVisitor.cpp +++ b/swift/extractor/visitors/TypeVisitor.cpp @@ -122,13 +122,13 @@ void TypeVisitor::visitParenType(swift::ParenType* type) { } codeql::OptionalType TypeVisitor::translateOptionalType(const swift::OptionalType& type) { - auto entry = createEntry(type); + auto entry = createTypeEntry(type); fillUnarySyntaxSugarType(type, entry); return entry; } codeql::ArraySliceType TypeVisitor::translateArraySliceType(const swift::ArraySliceType& type) { - auto entry = createEntry(type); + auto entry = createTypeEntry(type); fillUnarySyntaxSugarType(type, entry); return entry; } @@ -163,7 +163,7 @@ void TypeVisitor::visitLValueType(swift::LValueType* type) { codeql::PrimaryArchetypeType TypeVisitor::translatePrimaryArchetypeType( const swift::PrimaryArchetypeType& type) { - auto entry = createEntry(type); + auto entry = createTypeEntry(type); fillArchetypeType(type, entry); return entry; } @@ -229,7 +229,7 @@ void TypeVisitor::emitAnyGenericType(swift::AnyGenericType* type, codeql::NestedArchetypeType TypeVisitor::translateNestedArchetypeType( const swift::NestedArchetypeType& type) { - auto entry = createEntry(type); + auto entry = createTypeEntry(type); entry.parent = dispatcher_.fetchLabel(type.getParent()); entry.associated_type_declaration = dispatcher_.fetchLabel(type.getAssocType()); fillArchetypeType(type, entry); @@ -248,26 +248,26 @@ void TypeVisitor::fillArchetypeType(const swift::ArchetypeType& type, ArchetypeT } codeql::ExistentialType TypeVisitor::translateExistentialType(const swift::ExistentialType& type) { - auto entry = createEntry(type); + auto entry = createTypeEntry(type); entry.constraint = dispatcher_.fetchLabel(type.getConstraintType()); return entry; } codeql::DynamicSelfType TypeVisitor::translateDynamicSelfType(const swift::DynamicSelfType& type) { - auto entry = createEntry(type); + auto entry = createTypeEntry(type); entry.static_self_type = dispatcher_.fetchLabel(type.getSelfType()); return entry; } codeql::VariadicSequenceType TypeVisitor::translateVariadicSequenceType( const swift::VariadicSequenceType& type) { - auto entry = createEntry(type); + auto entry = createTypeEntry(type); fillUnarySyntaxSugarType(type, entry); return entry; } codeql::InOutType TypeVisitor::translateInOutType(const swift::InOutType& type) { - auto entry = createEntry(type); + auto entry = createTypeEntry(type); entry.object_type = dispatcher_.fetchLabel(type.getObjectType()); return entry; } @@ -300,19 +300,19 @@ void TypeVisitor::fillReferenceStorageType(const swift::ReferenceStorageType& ty codeql::ProtocolCompositionType TypeVisitor::translateProtocolCompositionType( const swift::ProtocolCompositionType& type) { - auto entry = createEntry(type); + auto entry = createTypeEntry(type); entry.members = dispatcher_.fetchRepeatedLabels(type.getMembers()); return entry; } codeql::BuiltinIntegerLiteralType TypeVisitor::translateBuiltinIntegerLiteralType( const swift::BuiltinIntegerLiteralType& type) { - return createEntry(type); + return createTypeEntry(type); } codeql::BuiltinIntegerType TypeVisitor::translateBuiltinIntegerType( const swift::BuiltinIntegerType& type) { - auto entry = createEntry(type); + auto entry = createTypeEntry(type); if (type.isFixedWidth()) { entry.width = type.getFixedWidth(); } @@ -321,51 +321,51 @@ codeql::BuiltinIntegerType TypeVisitor::translateBuiltinIntegerType( codeql::BuiltinBridgeObjectType TypeVisitor::translateBuiltinBridgeObjectType( const swift::BuiltinBridgeObjectType& type) { - return createEntry(type); + return createTypeEntry(type); } codeql::BuiltinDefaultActorStorageType TypeVisitor::translateBuiltinDefaultActorStorageType( const swift::BuiltinDefaultActorStorageType& type) { - return createEntry(type); + return createTypeEntry(type); } codeql::BuiltinExecutorType TypeVisitor::translateBuiltinExecutorType( const swift::BuiltinExecutorType& type) { - return createEntry(type); + return createTypeEntry(type); } codeql::BuiltinFloatType TypeVisitor::translateBuiltinFloatType( const swift::BuiltinFloatType& type) { - return createEntry(type); + return createTypeEntry(type); } codeql::BuiltinJobType TypeVisitor::translateBuiltinJobType(const swift::BuiltinJobType& type) { - return createEntry(type); + return createTypeEntry(type); } codeql::BuiltinNativeObjectType TypeVisitor::translateBuiltinNativeObjectType( const swift::BuiltinNativeObjectType& type) { - return createEntry(type); + return createTypeEntry(type); } codeql::BuiltinRawPointerType TypeVisitor::translateBuiltinRawPointerType( const swift::BuiltinRawPointerType& type) { - return createEntry(type); + return createTypeEntry(type); } codeql::BuiltinRawUnsafeContinuationType TypeVisitor::translateBuiltinRawUnsafeContinuationType( const swift::BuiltinRawUnsafeContinuationType& type) { - return createEntry(type); + return createTypeEntry(type); } codeql::BuiltinUnsafeValueBufferType TypeVisitor::translateBuiltinUnsafeValueBufferType( const swift::BuiltinUnsafeValueBufferType& type) { - return createEntry(type); + return createTypeEntry(type); } codeql::BuiltinVectorType TypeVisitor::translateBuiltinVectorType( const swift::BuiltinVectorType& type) { - return createEntry(type); + return createTypeEntry(type); } } // namespace codeql diff --git a/swift/extractor/visitors/TypeVisitor.h b/swift/extractor/visitors/TypeVisitor.h index 99d1ca7fdbb..77ae8ee13bf 100644 --- a/swift/extractor/visitors/TypeVisitor.h +++ b/swift/extractor/visitors/TypeVisitor.h @@ -81,8 +81,8 @@ class TypeVisitor : public TypeVisitorBase { void emitAnyGenericType(swift::AnyGenericType* type, TrapLabel label); template - auto createEntry(const T& type) { - TrapClassOf entry{dispatcher_.assignNewLabel(type)}; + auto createTypeEntry(const T& type) { + auto entry = dispatcher_.createEntry(type); fillType(type, entry); return entry; } From 3a975174c38001553c02144c7654651d5d99399e Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Fri, 1 Jul 2022 15:17:21 +0200 Subject: [PATCH 121/505] Swift: extract `ImportDecl` and `ModuleDecl` As `ASTMangler` crashes when called on `ModuleDecl`, we simply use its name. This might probably not work reliably in a scenario where multiple modules are compiled with the same name (like `main`), but this is left for future work. At the moment this cannot create DB inconsistencies. --- swift/codegen/schema.yml | 5 +++ swift/extractor/SwiftExtractor.cpp | 21 +++++++---- swift/extractor/infra/SwiftDispatcher.h | 9 ++++- swift/extractor/trap/TrapOutput.h | 4 +-- swift/extractor/visitors/DeclVisitor.cpp | 36 +++++++++++++++---- swift/extractor/visitors/DeclVisitor.h | 11 ++++++ .../swift/generated/decl/ImportDecl.qll | 22 ++++++++++++ .../swift/generated/decl/ModuleDecl.qll | 4 +++ swift/ql/lib/swift.dbscheme | 25 ++++++++++++- swift/ql/test/TestUtils.qll | 2 ++ .../decl/ImportDecl/ImportDecl.expected | 5 +++ .../generated/decl/ImportDecl/ImportDecl.ql | 11 ++++++ .../ImportDecl_getDeclaration.expected | 5 +++ .../ImportDecl/ImportDecl_getDeclaration.ql | 7 ++++ .../decl/ImportDecl/MISSING_SOURCE.txt | 4 --- .../generated/decl/ImportDecl/import.swift | 5 +++ .../decl/ModuleDecl/MISSING_SOURCE.txt | 4 --- .../decl/ModuleDecl/ModuleDecl.expected | 1 + .../generated/decl/ModuleDecl/ModuleDecl.ql | 15 ++++++++ .../ModuleDecl_getBaseType.expected | 0 .../decl/ModuleDecl/ModuleDecl_getBaseType.ql | 7 ++++ .../generated/decl/ModuleDecl/modules.swift | 1 + 22 files changed, 180 insertions(+), 24 deletions(-) create mode 100644 swift/ql/test/extractor-tests/generated/decl/ImportDecl/ImportDecl.expected create mode 100644 swift/ql/test/extractor-tests/generated/decl/ImportDecl/ImportDecl.ql create mode 100644 swift/ql/test/extractor-tests/generated/decl/ImportDecl/ImportDecl_getDeclaration.expected create mode 100644 swift/ql/test/extractor-tests/generated/decl/ImportDecl/ImportDecl_getDeclaration.ql delete mode 100644 swift/ql/test/extractor-tests/generated/decl/ImportDecl/MISSING_SOURCE.txt create mode 100644 swift/ql/test/extractor-tests/generated/decl/ImportDecl/import.swift delete mode 100644 swift/ql/test/extractor-tests/generated/decl/ModuleDecl/MISSING_SOURCE.txt create mode 100644 swift/ql/test/extractor-tests/generated/decl/ModuleDecl/ModuleDecl.expected create mode 100644 swift/ql/test/extractor-tests/generated/decl/ModuleDecl/ModuleDecl.ql create mode 100644 swift/ql/test/extractor-tests/generated/decl/ModuleDecl/ModuleDecl_getBaseType.expected create mode 100644 swift/ql/test/extractor-tests/generated/decl/ModuleDecl/ModuleDecl_getBaseType.ql create mode 100644 swift/ql/test/extractor-tests/generated/decl/ModuleDecl/modules.swift diff --git a/swift/codegen/schema.yml b/swift/codegen/schema.yml index f2e7837f2b2..01bb0c2feba 100644 --- a/swift/codegen/schema.yml +++ b/swift/codegen/schema.yml @@ -273,6 +273,9 @@ IfConfigDecl: ImportDecl: _extends: Decl + is_exported: predicate + module: ModuleDecl + declarations: ValueDecl* MissingMemberDecl: _extends: Decl @@ -1067,6 +1070,8 @@ GenericTypeDecl: ModuleDecl: _extends: TypeDecl + is_builtin_module: predicate + is_system_module: predicate ConstructorRefCallExpr: _extends: SelfApplyExpr diff --git a/swift/extractor/SwiftExtractor.cpp b/swift/extractor/SwiftExtractor.cpp index 65f0ae150cc..dd8b2569e3c 100644 --- a/swift/extractor/SwiftExtractor.cpp +++ b/swift/extractor/SwiftExtractor.cpp @@ -64,8 +64,19 @@ static std::string getTrapFilename(swift::ModuleDecl& module, swift::SourceFile* return filename; } +static llvm::SmallVector getTopLevelDecls(swift::ModuleDecl& module, + swift::SourceFile* primaryFile = nullptr) { + llvm::SmallVector ret; + ret.push_back(&module); + if (primaryFile) { + primaryFile->getTopLevelDecls(ret); + } else { + module.getTopLevelDecls(ret); + } + return ret; +} + static void extractDeclarations(const SwiftExtractorConfiguration& config, - llvm::ArrayRef topLevelDecls, swift::CompilerInstance& compiler, swift::ModuleDecl& module, swift::SourceFile* primaryFile = nullptr) { @@ -119,6 +130,7 @@ static void extractDeclarations(const SwiftExtractorConfiguration& config, trap.emit(LocationsTrap{unknownLocationLabel, unknownFileLabel}); SwiftVisitor visitor(compiler.getSourceMgr(), arena, trap, module, primaryFile); + auto topLevelDecls = getTopLevelDecls(module, primaryFile); for (auto decl : topLevelDecls) { visitor.extract(decl); } @@ -203,10 +215,7 @@ void codeql::extractSwiftFiles(const SwiftExtractorConfiguration& config, // user code twice: once during the module build in a form of a source file, and then as // a pre-built module during building of the dependent source files. if (module->isSystemModule() || module->isBuiltinModule()) { - llvm::SmallVector decls; - module->getTopLevelDecls(decls); - // TODO: pass ModuleDecl directly when we have module extraction in place? - extractDeclarations(config, decls, compiler, *module); + extractDeclarations(config, compiler, *module); } else { for (auto file : module->getFiles()) { auto sourceFile = llvm::dyn_cast(file); @@ -214,7 +223,7 @@ void codeql::extractSwiftFiles(const SwiftExtractorConfiguration& config, continue; } archiveFile(config, *sourceFile); - extractDeclarations(config, sourceFile->getTopLevelDecls(), compiler, *module, sourceFile); + extractDeclarations(config, compiler, *module, sourceFile); } } } diff --git a/swift/extractor/infra/SwiftDispatcher.h b/swift/extractor/infra/SwiftDispatcher.h index 525cb74330a..c7ca43a1614 100644 --- a/swift/extractor/infra/SwiftDispatcher.h +++ b/swift/extractor/infra/SwiftDispatcher.h @@ -179,6 +179,11 @@ class SwiftDispatcher { return ret; } + template + void emitDebugInfo(const Args&... args) { + trap.debug(std::forward(args)...); + } + // In order to not emit duplicated entries for declarations, we restrict emission to only // Decls declared within the current "scope". // Depending on the whether we are extracting a primary source file or not the scope is defined as @@ -192,7 +197,9 @@ class SwiftDispatcher { if (decl.getModuleContext() != ¤tModule) { return false; } - if (!currentPrimarySourceFile) { + // ModuleDecl is a special case: if it passed the previous test, it is the current module + // but it never has a source file, so we short circuit to emit it in any case + if (!currentPrimarySourceFile || decl.getKind() == swift::DeclKind::Module) { return true; } if (auto context = decl.getDeclContext()) { diff --git a/swift/extractor/trap/TrapOutput.h b/swift/extractor/trap/TrapOutput.h index b9104b9118a..0e3f61d39e7 100644 --- a/swift/extractor/trap/TrapOutput.h +++ b/swift/extractor/trap/TrapOutput.h @@ -41,8 +41,8 @@ class TrapOutput { template void debug(const Args&... args) { - out_ << "// DEBUG: "; - (out_ << ... << args) << '\n'; + out_ << "/* DEBUG:\n"; + (out_ << ... << args) << "\n*/\n"; } private: diff --git a/swift/extractor/visitors/DeclVisitor.cpp b/swift/extractor/visitors/DeclVisitor.cpp index ed47f1e5fb1..33f746f6634 100644 --- a/swift/extractor/visitors/DeclVisitor.cpp +++ b/swift/extractor/visitors/DeclVisitor.cpp @@ -241,16 +241,15 @@ std::variant DeclVisitor::trans std::optional DeclVisitor::translateSubscriptDecl( const swift::SubscriptDecl& decl) { - auto id = dispatcher_.assignNewLabel(decl, mangledName(decl)); - if (!dispatcher_.shouldEmitDeclBody(decl)) { + auto entry = createNamedEntry(decl); + if (!entry) { return std::nullopt; } - SubscriptDecl entry{id}; - entry.element_type = dispatcher_.fetchLabel(decl.getElementInterfaceType()); + entry->element_type = dispatcher_.fetchLabel(decl.getElementInterfaceType()); if (auto indices = decl.getIndices()) { - entry.params = dispatcher_.fetchRepeatedLabels(*indices); + entry->params = dispatcher_.fetchRepeatedLabels(*indices); } - fillAbstractStorageDecl(decl, entry); + fillAbstractStorageDecl(decl, *entry); return entry; } @@ -262,7 +261,32 @@ codeql::ExtensionDecl DeclVisitor::translateExtensionDecl(const swift::Extension return entry; } +codeql::ImportDecl DeclVisitor::translateImportDecl(const swift::ImportDecl& decl) { + auto entry = dispatcher_.createEntry(decl); + entry.is_exported = decl.isExported(); + entry.module = dispatcher_.fetchLabel(decl.getModule()); + entry.declarations = dispatcher_.fetchRepeatedLabels(decl.getDecls()); + return entry; +} + +std::optional DeclVisitor::translateModuleDecl(const swift::ModuleDecl& decl) { + auto entry = createNamedEntry(decl); + if (!entry) { + return std::nullopt; + } + entry->is_builtin_module = decl.isBuiltinModule(); + entry->is_system_module = decl.isSystemModule(); + fillTypeDecl(decl, *entry); + return entry; +} + std::string DeclVisitor::mangledName(const swift::ValueDecl& decl) { + // ASTMangler::mangleAnyDecl crashes when called on `ModuleDecl` + // TODO find a more unique string working also when different modules are compiled with the same + // name + if (decl.getKind() == swift::DeclKind::Module) { + return static_cast(decl).getRealName().str().str(); + } // prefix adds a couple of special symbols, we don't necessary need them return mangler.mangleAnyDecl(&decl, /* prefix = */ false); } diff --git a/swift/extractor/visitors/DeclVisitor.h b/swift/extractor/visitors/DeclVisitor.h index 0527513d859..c9a1b43556d 100644 --- a/swift/extractor/visitors/DeclVisitor.h +++ b/swift/extractor/visitors/DeclVisitor.h @@ -50,6 +50,8 @@ class DeclVisitor : public AstVisitorBase { const swift::AccessorDecl& decl); std::optional translateSubscriptDecl(const swift::SubscriptDecl& decl); codeql::ExtensionDecl translateExtensionDecl(const swift::ExtensionDecl& decl); + codeql::ImportDecl translateImportDecl(const swift::ImportDecl& decl); + std::optional translateModuleDecl(const swift::ModuleDecl& decl); private: std::string mangledName(const swift::ValueDecl& decl); @@ -66,6 +68,15 @@ class DeclVisitor : public AstVisitorBase { void fillAbstractStorageDecl(const swift::AbstractStorageDecl& decl, codeql::AbstractStorageDecl& entry); + template + std::optional> createNamedEntry(const D& decl) { + auto id = dispatcher_.assignNewLabel(decl, mangledName(decl)); + if (dispatcher_.shouldEmitDeclBody(decl)) { + return TrapClassOf{id}; + } + return std::nullopt; + } + private: swift::Mangle::ASTMangler mangler; }; diff --git a/swift/ql/lib/codeql/swift/generated/decl/ImportDecl.qll b/swift/ql/lib/codeql/swift/generated/decl/ImportDecl.qll index fdd89db170a..8f5638ad069 100644 --- a/swift/ql/lib/codeql/swift/generated/decl/ImportDecl.qll +++ b/swift/ql/lib/codeql/swift/generated/decl/ImportDecl.qll @@ -1,6 +1,28 @@ // generated by codegen/codegen.py import codeql.swift.elements.decl.Decl +import codeql.swift.elements.decl.ModuleDecl +import codeql.swift.elements.decl.ValueDecl class ImportDeclBase extends @import_decl, Decl { override string getAPrimaryQlClass() { result = "ImportDecl" } + + predicate isExported() { import_decl_is_exported(this) } + + ModuleDecl getModule() { + exists(ModuleDecl x | + import_decls(this, x) and + result = x.resolve() + ) + } + + ValueDecl getDeclaration(int index) { + exists(ValueDecl x | + import_decl_declarations(this, index, x) and + result = x.resolve() + ) + } + + ValueDecl getADeclaration() { result = getDeclaration(_) } + + int getNumberOfDeclarations() { result = count(getADeclaration()) } } diff --git a/swift/ql/lib/codeql/swift/generated/decl/ModuleDecl.qll b/swift/ql/lib/codeql/swift/generated/decl/ModuleDecl.qll index aad5d00c0c6..3a1931b2d52 100644 --- a/swift/ql/lib/codeql/swift/generated/decl/ModuleDecl.qll +++ b/swift/ql/lib/codeql/swift/generated/decl/ModuleDecl.qll @@ -3,4 +3,8 @@ import codeql.swift.elements.decl.TypeDecl class ModuleDeclBase extends @module_decl, TypeDecl { override string getAPrimaryQlClass() { result = "ModuleDecl" } + + predicate isBuiltinModule() { module_decl_is_builtin_module(this) } + + predicate isSystemModule() { module_decl_is_system_module(this) } } diff --git a/swift/ql/lib/swift.dbscheme b/swift/ql/lib/swift.dbscheme index 7937e938e70..83ee8412131 100644 --- a/swift/ql/lib/swift.dbscheme +++ b/swift/ql/lib/swift.dbscheme @@ -644,7 +644,20 @@ if_config_decls( //dir=decl ); import_decls( //dir=decl - unique int id: @import_decl + unique int id: @import_decl, + int module: @module_decl ref +); + +#keyset[id] +import_decl_is_exported( //dir=decl + int id: @import_decl ref +); + +#keyset[id, index] +import_decl_declarations( //dir=decl + int id: @import_decl ref, + int index: int ref, + int declaration: @value_decl ref ); missing_member_decls( //dir=decl @@ -2018,6 +2031,16 @@ module_decls( //dir=decl unique int id: @module_decl ); +#keyset[id] +module_decl_is_builtin_module( //dir=decl + int id: @module_decl ref +); + +#keyset[id] +module_decl_is_system_module( //dir=decl + int id: @module_decl ref +); + constructor_ref_call_exprs( //dir=expr unique int id: @constructor_ref_call_expr ); diff --git a/swift/ql/test/TestUtils.qll b/swift/ql/test/TestUtils.qll index 9359944fe90..0c04fcb5989 100644 --- a/swift/ql/test/TestUtils.qll +++ b/swift/ql/test/TestUtils.qll @@ -4,6 +4,8 @@ cached predicate toBeTested(Element e) { e instanceof File or + exists(ModuleDecl m | m = e and not m.isBuiltinModule() and not m.isSystemModule()) + or exists(Locatable loc | loc.getLocation().getFile().getName().matches("%swift/ql/test%") and ( diff --git a/swift/ql/test/extractor-tests/generated/decl/ImportDecl/ImportDecl.expected b/swift/ql/test/extractor-tests/generated/decl/ImportDecl/ImportDecl.expected new file mode 100644 index 00000000000..3e9fd295744 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ImportDecl/ImportDecl.expected @@ -0,0 +1,5 @@ +| import.swift:1:1:1:8 | import ... | isExported: | no | getModule: | file://:0:0:0:0 | Swift | +| import.swift:2:1:2:24 | import ... | isExported: | no | getModule: | file://:0:0:0:0 | Swift | +| import.swift:3:12:3:32 | import ... | isExported: | yes | getModule: | file://:0:0:0:0 | Swift | +| import.swift:4:1:4:19 | import ... | isExported: | no | getModule: | file://:0:0:0:0 | Swift | +| import.swift:5:1:5:23 | import ... | isExported: | no | getModule: | file://:0:0:0:0 | Swift | diff --git a/swift/ql/test/extractor-tests/generated/decl/ImportDecl/ImportDecl.ql b/swift/ql/test/extractor-tests/generated/decl/ImportDecl/ImportDecl.ql new file mode 100644 index 00000000000..34978aae118 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ImportDecl/ImportDecl.ql @@ -0,0 +1,11 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from ImportDecl x, string isExported, ModuleDecl getModule +where + toBeTested(x) and + not x.isUnknown() and + (if x.isExported() then isExported = "yes" else isExported = "no") and + getModule = x.getModule() +select x, "isExported:", isExported, "getModule:", getModule diff --git a/swift/ql/test/extractor-tests/generated/decl/ImportDecl/ImportDecl_getDeclaration.expected b/swift/ql/test/extractor-tests/generated/decl/ImportDecl/ImportDecl_getDeclaration.expected new file mode 100644 index 00000000000..b877b113bc6 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ImportDecl/ImportDecl_getDeclaration.expected @@ -0,0 +1,5 @@ +| import.swift:2:1:2:24 | import ... | 0 | file://:0:0:0:0 | Int | +| import.swift:3:12:3:32 | import ... | 0 | file://:0:0:0:0 | Double | +| import.swift:4:1:4:19 | import ... | 0 | file://:0:0:0:0 | print(_:separator:terminator:) | +| import.swift:4:1:4:19 | import ... | 1 | file://:0:0:0:0 | print(_:separator:terminator:to:) | +| import.swift:5:1:5:23 | import ... | 0 | file://:0:0:0:0 | Hashable | diff --git a/swift/ql/test/extractor-tests/generated/decl/ImportDecl/ImportDecl_getDeclaration.ql b/swift/ql/test/extractor-tests/generated/decl/ImportDecl/ImportDecl_getDeclaration.ql new file mode 100644 index 00000000000..852fe518ed2 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ImportDecl/ImportDecl_getDeclaration.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from ImportDecl x, int index +where toBeTested(x) and not x.isUnknown() +select x, index, x.getDeclaration(index) diff --git a/swift/ql/test/extractor-tests/generated/decl/ImportDecl/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/decl/ImportDecl/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/decl/ImportDecl/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/decl/ImportDecl/import.swift b/swift/ql/test/extractor-tests/generated/decl/ImportDecl/import.swift new file mode 100644 index 00000000000..bcb562996ae --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ImportDecl/import.swift @@ -0,0 +1,5 @@ +import Swift +import typealias Swift.Int +@_exported import struct Swift.Double +import func Swift.print // imports all overloads +import protocol Swift.Hashable diff --git a/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/ModuleDecl.expected b/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/ModuleDecl.expected new file mode 100644 index 00000000000..ef50edbe828 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/ModuleDecl.expected @@ -0,0 +1 @@ +| file://:0:0:0:0 | Foo | getInterfaceType: | module | getName: | Foo | isBuiltinModule: | no | isSystemModule: | no | diff --git a/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/ModuleDecl.ql b/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/ModuleDecl.ql new file mode 100644 index 00000000000..73e912a8245 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/ModuleDecl.ql @@ -0,0 +1,15 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from + ModuleDecl x, Type getInterfaceType, string getName, string isBuiltinModule, string isSystemModule +where + toBeTested(x) and + not x.isUnknown() and + getInterfaceType = x.getInterfaceType() and + getName = x.getName() and + (if x.isBuiltinModule() then isBuiltinModule = "yes" else isBuiltinModule = "no") and + if x.isSystemModule() then isSystemModule = "yes" else isSystemModule = "no" +select x, "getInterfaceType:", getInterfaceType, "getName:", getName, "isBuiltinModule:", + isBuiltinModule, "isSystemModule:", isSystemModule diff --git a/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/ModuleDecl_getBaseType.expected b/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/ModuleDecl_getBaseType.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/ModuleDecl_getBaseType.ql b/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/ModuleDecl_getBaseType.ql new file mode 100644 index 00000000000..0c0cec75d86 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/ModuleDecl_getBaseType.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from ModuleDecl x, int index +where toBeTested(x) and not x.isUnknown() +select x, index, x.getBaseType(index) diff --git a/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/modules.swift b/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/modules.swift new file mode 100644 index 00000000000..bb12ed63ddd --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/modules.swift @@ -0,0 +1 @@ +//codeql-extractor-options: -module-name Foo From f9143f7855ab00b464b42320e54acd5366690898 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Fri, 1 Jul 2022 15:43:16 +0200 Subject: [PATCH 122/505] Swift: fix extraction of empty files --- swift/extractor/SwiftExtractor.cpp | 5 ++++- swift/ql/test/extractor-tests/generated/File/File.expected | 3 ++- .../generated/File/{hello.swift => empty.swift} | 0 swift/ql/test/extractor-tests/generated/File/non_empty.swift | 1 + 4 files changed, 7 insertions(+), 2 deletions(-) rename swift/ql/test/extractor-tests/generated/File/{hello.swift => empty.swift} (100%) create mode 100644 swift/ql/test/extractor-tests/generated/File/non_empty.swift diff --git a/swift/extractor/SwiftExtractor.cpp b/swift/extractor/SwiftExtractor.cpp index dd8b2569e3c..44d2309cb2a 100644 --- a/swift/extractor/SwiftExtractor.cpp +++ b/swift/extractor/SwiftExtractor.cpp @@ -134,7 +134,10 @@ static void extractDeclarations(const SwiftExtractorConfiguration& config, for (auto decl : topLevelDecls) { visitor.extract(decl); } - if (topLevelDecls.empty()) { + // TODO the following will be moved to the dispatcher when we start caching swift file objects + // for the moment, topLevelDecls always contains the current module, which does not have a file + // associated with it, so we need a special case when there are no top level declarations + if (topLevelDecls.size() == 1) { // In the case of empty files, the dispatcher is not called, but we still want to 'record' the // fact that the file was extracted llvm::SmallString name(filename); diff --git a/swift/ql/test/extractor-tests/generated/File/File.expected b/swift/ql/test/extractor-tests/generated/File/File.expected index d2ef49d491d..73437ea7a92 100644 --- a/swift/ql/test/extractor-tests/generated/File/File.expected +++ b/swift/ql/test/extractor-tests/generated/File/File.expected @@ -1,2 +1,3 @@ +| empty.swift:0:0:0:0 | empty.swift | getName: | empty.swift | | file://:0:0:0:0 | | getName: | | -| hello.swift:0:0:0:0 | hello.swift | getName: | hello.swift | +| non_empty.swift:0:0:0:0 | non_empty.swift | getName: | non_empty.swift | diff --git a/swift/ql/test/extractor-tests/generated/File/hello.swift b/swift/ql/test/extractor-tests/generated/File/empty.swift similarity index 100% rename from swift/ql/test/extractor-tests/generated/File/hello.swift rename to swift/ql/test/extractor-tests/generated/File/empty.swift diff --git a/swift/ql/test/extractor-tests/generated/File/non_empty.swift b/swift/ql/test/extractor-tests/generated/File/non_empty.swift new file mode 100644 index 00000000000..11b15b1a458 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/File/non_empty.swift @@ -0,0 +1 @@ +print("hello") From e575bab9d6d0288c4ba136a6fb2cacfdca8b05b4 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Fri, 1 Jul 2022 15:45:28 +0200 Subject: [PATCH 123/505] Revert unwanted committed files --- .../org/apache/commons/collections4/map/AbstractHashedMap.java | 2 +- .../org/apache/commons/collections4/map/AbstractLinkedMap.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/java/ql/test/stubs/apache-commons-collections4-4.4/org/apache/commons/collections4/map/AbstractHashedMap.java b/java/ql/test/stubs/apache-commons-collections4-4.4/org/apache/commons/collections4/map/AbstractHashedMap.java index 35d673df2c8..f79236e4f10 100644 --- a/java/ql/test/stubs/apache-commons-collections4-4.4/org/apache/commons/collections4/map/AbstractHashedMap.java +++ b/java/ql/test/stubs/apache-commons-collections4-4.4/org/apache/commons/collections4/map/AbstractHashedMap.java @@ -30,7 +30,7 @@ public class AbstractHashedMap extends AbstractMap implements Iterab protected AbstractHashedMap(int p0){} protected AbstractHashedMap(int p0, float p1){} protected AbstractHashedMap(int p0, float p1, int p2){} - protected AbstractHashedMap.HashEntry createTypeEntry(AbstractHashedMap.HashEntry p0, int p1, K p2, V p3){ return null; } + protected AbstractHashedMap.HashEntry createEntry(AbstractHashedMap.HashEntry p0, int p1, K p2, V p3){ return null; } protected AbstractHashedMap.HashEntry entryNext(AbstractHashedMap.HashEntry p0){ return null; } protected AbstractHashedMap.HashEntry getEntry(Object p0){ return null; } protected AbstractHashedMap clone(){ return null; } diff --git a/java/ql/test/stubs/apache-commons-collections4-4.4/org/apache/commons/collections4/map/AbstractLinkedMap.java b/java/ql/test/stubs/apache-commons-collections4-4.4/org/apache/commons/collections4/map/AbstractLinkedMap.java index 6d349ee1130..b96404dd5b9 100644 --- a/java/ql/test/stubs/apache-commons-collections4-4.4/org/apache/commons/collections4/map/AbstractLinkedMap.java +++ b/java/ql/test/stubs/apache-commons-collections4-4.4/org/apache/commons/collections4/map/AbstractLinkedMap.java @@ -16,7 +16,7 @@ abstract public class AbstractLinkedMap extends AbstractHashedMap im protected AbstractLinkedMap(int p0){} protected AbstractLinkedMap(int p0, float p1){} protected AbstractLinkedMap(int p0, float p1, int p2){} - protected AbstractLinkedMap.LinkEntry createTypeEntry(AbstractHashedMap.HashEntry p0, int p1, K p2, V p3){ return null; } + protected AbstractLinkedMap.LinkEntry createEntry(AbstractHashedMap.HashEntry p0, int p1, K p2, V p3){ return null; } protected AbstractLinkedMap.LinkEntry entryAfter(AbstractLinkedMap.LinkEntry p0){ return null; } protected AbstractLinkedMap.LinkEntry entryBefore(AbstractLinkedMap.LinkEntry p0){ return null; } protected AbstractLinkedMap.LinkEntry getEntry(Object p0){ return null; } From 8addc0679949d762820d365de03299ff61f79ee3 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Fri, 1 Jul 2022 15:59:36 +0200 Subject: [PATCH 124/505] Swift: add integration test for multiple modules --- .../posix-only/partial-modules/Modules.expected | 4 ++++ .../integration-tests/posix-only/partial-modules/Modules.ql | 5 +++++ 2 files changed, 9 insertions(+) create mode 100644 swift/integration-tests/posix-only/partial-modules/Modules.expected create mode 100644 swift/integration-tests/posix-only/partial-modules/Modules.ql diff --git a/swift/integration-tests/posix-only/partial-modules/Modules.expected b/swift/integration-tests/posix-only/partial-modules/Modules.expected new file mode 100644 index 00000000000..4c738975f34 --- /dev/null +++ b/swift/integration-tests/posix-only/partial-modules/Modules.expected @@ -0,0 +1,4 @@ +| file://:0:0:0:0 | A | +| file://:0:0:0:0 | B | +| file://:0:0:0:0 | main | +| file://:0:0:0:0 | partial_modules | diff --git a/swift/integration-tests/posix-only/partial-modules/Modules.ql b/swift/integration-tests/posix-only/partial-modules/Modules.ql new file mode 100644 index 00000000000..d456e261a3c --- /dev/null +++ b/swift/integration-tests/posix-only/partial-modules/Modules.ql @@ -0,0 +1,5 @@ +import swift + +from ModuleDecl decl +where not decl.isBuiltinModule() and not decl.isSystemModule() +select decl From 416977dc5003fabc74dc09190f91f8374c8dc0fe Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 1 Jul 2022 09:27:12 +0100 Subject: [PATCH 125/505] Swift: Add test cases for removeFirst, removeLast. --- .../CWE-135/StringLengthConflation.expected | 40 +++++++++---------- .../CWE-135/StringLengthConflation.swift | 12 ++++++ 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.expected b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.expected index 1bb4de15325..8e42c4a145d 100644 --- a/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.expected +++ b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.expected @@ -1,36 +1,36 @@ edges -| StringLengthConflation.swift:101:34:101:36 | .count : | StringLengthConflation.swift:101:34:101:44 | ... call to -(_:_:) ... | -| StringLengthConflation.swift:102:36:102:38 | .count : | StringLengthConflation.swift:102:36:102:46 | ... call to -(_:_:) ... | -| StringLengthConflation.swift:107:36:107:38 | .count : | StringLengthConflation.swift:107:36:107:46 | ... call to -(_:_:) ... | -| StringLengthConflation.swift:108:38:108:40 | .count : | StringLengthConflation.swift:108:38:108:48 | ... call to -(_:_:) ... | | StringLengthConflation.swift:113:34:113:36 | .count : | StringLengthConflation.swift:113:34:113:44 | ... call to -(_:_:) ... | | StringLengthConflation.swift:114:36:114:38 | .count : | StringLengthConflation.swift:114:36:114:46 | ... call to -(_:_:) ... | -| StringLengthConflation.swift:120:28:120:30 | .count : | StringLengthConflation.swift:120:28:120:38 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:119:36:119:38 | .count : | StringLengthConflation.swift:119:36:119:46 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:120:38:120:40 | .count : | StringLengthConflation.swift:120:38:120:48 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:125:34:125:36 | .count : | StringLengthConflation.swift:125:34:125:44 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:126:36:126:38 | .count : | StringLengthConflation.swift:126:36:126:46 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:132:28:132:30 | .count : | StringLengthConflation.swift:132:28:132:38 | ... call to -(_:_:) ... | nodes | StringLengthConflation.swift:72:33:72:35 | .count | semmle.label | .count | | StringLengthConflation.swift:78:47:78:49 | .count | semmle.label | .count | -| StringLengthConflation.swift:101:34:101:36 | .count : | semmle.label | .count : | -| StringLengthConflation.swift:101:34:101:44 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | -| StringLengthConflation.swift:102:36:102:38 | .count : | semmle.label | .count : | -| StringLengthConflation.swift:102:36:102:46 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | -| StringLengthConflation.swift:107:36:107:38 | .count : | semmle.label | .count : | -| StringLengthConflation.swift:107:36:107:46 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | -| StringLengthConflation.swift:108:38:108:40 | .count : | semmle.label | .count : | -| StringLengthConflation.swift:108:38:108:48 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | | StringLengthConflation.swift:113:34:113:36 | .count : | semmle.label | .count : | | StringLengthConflation.swift:113:34:113:44 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | | StringLengthConflation.swift:114:36:114:38 | .count : | semmle.label | .count : | | StringLengthConflation.swift:114:36:114:46 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | -| StringLengthConflation.swift:120:28:120:30 | .count : | semmle.label | .count : | -| StringLengthConflation.swift:120:28:120:38 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:119:36:119:38 | .count : | semmle.label | .count : | +| StringLengthConflation.swift:119:36:119:46 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:120:38:120:40 | .count : | semmle.label | .count : | +| StringLengthConflation.swift:120:38:120:48 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:125:34:125:36 | .count : | semmle.label | .count : | +| StringLengthConflation.swift:125:34:125:44 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:126:36:126:38 | .count : | semmle.label | .count : | +| StringLengthConflation.swift:126:36:126:46 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:132:28:132:30 | .count : | semmle.label | .count : | +| StringLengthConflation.swift:132:28:132:38 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | subpaths #select | StringLengthConflation.swift:72:33:72:35 | .count | StringLengthConflation.swift:72:33:72:35 | .count | StringLengthConflation.swift:72:33:72:35 | .count | This String length is used in an NSString, but it may not be equivalent. | | StringLengthConflation.swift:78:47:78:49 | .count | StringLengthConflation.swift:78:47:78:49 | .count | StringLengthConflation.swift:78:47:78:49 | .count | This String length is used in an NSString, but it may not be equivalent. | -| StringLengthConflation.swift:101:34:101:44 | ... call to -(_:_:) ... | StringLengthConflation.swift:101:34:101:36 | .count : | StringLengthConflation.swift:101:34:101:44 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | -| StringLengthConflation.swift:102:36:102:46 | ... call to -(_:_:) ... | StringLengthConflation.swift:102:36:102:38 | .count : | StringLengthConflation.swift:102:36:102:46 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | -| StringLengthConflation.swift:107:36:107:46 | ... call to -(_:_:) ... | StringLengthConflation.swift:107:36:107:38 | .count : | StringLengthConflation.swift:107:36:107:46 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | -| StringLengthConflation.swift:108:38:108:48 | ... call to -(_:_:) ... | StringLengthConflation.swift:108:38:108:40 | .count : | StringLengthConflation.swift:108:38:108:48 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | | StringLengthConflation.swift:113:34:113:44 | ... call to -(_:_:) ... | StringLengthConflation.swift:113:34:113:36 | .count : | StringLengthConflation.swift:113:34:113:44 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | | StringLengthConflation.swift:114:36:114:46 | ... call to -(_:_:) ... | StringLengthConflation.swift:114:36:114:38 | .count : | StringLengthConflation.swift:114:36:114:46 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | -| StringLengthConflation.swift:120:28:120:38 | ... call to -(_:_:) ... | StringLengthConflation.swift:120:28:120:30 | .count : | StringLengthConflation.swift:120:28:120:38 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | +| StringLengthConflation.swift:119:36:119:46 | ... call to -(_:_:) ... | StringLengthConflation.swift:119:36:119:38 | .count : | StringLengthConflation.swift:119:36:119:46 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | +| StringLengthConflation.swift:120:38:120:48 | ... call to -(_:_:) ... | StringLengthConflation.swift:120:38:120:40 | .count : | StringLengthConflation.swift:120:38:120:48 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | +| StringLengthConflation.swift:125:34:125:44 | ... call to -(_:_:) ... | StringLengthConflation.swift:125:34:125:36 | .count : | StringLengthConflation.swift:125:34:125:44 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | +| StringLengthConflation.swift:126:36:126:46 | ... call to -(_:_:) ... | StringLengthConflation.swift:126:36:126:38 | .count : | StringLengthConflation.swift:126:36:126:46 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | +| StringLengthConflation.swift:132:28:132:38 | ... call to -(_:_:) ... | StringLengthConflation.swift:132:28:132:30 | .count : | StringLengthConflation.swift:132:28:132:38 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | diff --git a/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.swift b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.swift index 8cb698009b6..7a083267aa4 100644 --- a/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.swift +++ b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.swift @@ -96,6 +96,18 @@ func test(s: String) { let str8 = s.suffix(ns.length - 1) // BAD: NSString length used in String [NOT DETECTED] print("suffix '\(str7)' / '\(str8)'") + var str9 = s + str9.removeFirst(s.count - 1) // GOOD + var str10 = s + str10.removeFirst(ns.length - 1) // BAD: NSString length used in String [NOT DETECTED] + print("removeFirst '\(str9)' / '\(str10)'") + + var str11 = s + str11.removeLast(s.count - 1) // GOOD + var str12 = s + str12.removeLast(ns.length - 1) // BAD: NSString length used in String [NOT DETECTED] + print("removeLast '\(str11)' / '\(str12)'") + let nstr1 = ns.character(at: ns.length - 1) // GOOD let nmstr1 = nms.character(at: nms.length - 1) // GOOD let nstr2 = ns.character(at: s.count - 1) // BAD: String length used in NSString From a306f312cd62388a2b0bf1ec346e548f0de0cfb4 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 1 Jul 2022 11:01:12 +0100 Subject: [PATCH 126/505] Swift: Add a test of converting Range to NSRange. --- .../CWE-135/StringLengthConflation.expected | 56 +++++++++---------- .../CWE-135/StringLengthConflation.swift | 11 +++- 2 files changed, 38 insertions(+), 29 deletions(-) diff --git a/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.expected b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.expected index 8e42c4a145d..2654d28d334 100644 --- a/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.expected +++ b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.expected @@ -1,36 +1,36 @@ edges -| StringLengthConflation.swift:113:34:113:36 | .count : | StringLengthConflation.swift:113:34:113:44 | ... call to -(_:_:) ... | -| StringLengthConflation.swift:114:36:114:38 | .count : | StringLengthConflation.swift:114:36:114:46 | ... call to -(_:_:) ... | -| StringLengthConflation.swift:119:36:119:38 | .count : | StringLengthConflation.swift:119:36:119:46 | ... call to -(_:_:) ... | -| StringLengthConflation.swift:120:38:120:40 | .count : | StringLengthConflation.swift:120:38:120:48 | ... call to -(_:_:) ... | -| StringLengthConflation.swift:125:34:125:36 | .count : | StringLengthConflation.swift:125:34:125:44 | ... call to -(_:_:) ... | -| StringLengthConflation.swift:126:36:126:38 | .count : | StringLengthConflation.swift:126:36:126:46 | ... call to -(_:_:) ... | -| StringLengthConflation.swift:132:28:132:30 | .count : | StringLengthConflation.swift:132:28:132:38 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:122:34:122:36 | .count : | StringLengthConflation.swift:122:34:122:44 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:123:36:123:38 | .count : | StringLengthConflation.swift:123:36:123:46 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:128:36:128:38 | .count : | StringLengthConflation.swift:128:36:128:46 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:129:38:129:40 | .count : | StringLengthConflation.swift:129:38:129:48 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:134:34:134:36 | .count : | StringLengthConflation.swift:134:34:134:44 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:135:36:135:38 | .count : | StringLengthConflation.swift:135:36:135:46 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:141:28:141:30 | .count : | StringLengthConflation.swift:141:28:141:38 | ... call to -(_:_:) ... | nodes | StringLengthConflation.swift:72:33:72:35 | .count | semmle.label | .count | | StringLengthConflation.swift:78:47:78:49 | .count | semmle.label | .count | -| StringLengthConflation.swift:113:34:113:36 | .count : | semmle.label | .count : | -| StringLengthConflation.swift:113:34:113:44 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | -| StringLengthConflation.swift:114:36:114:38 | .count : | semmle.label | .count : | -| StringLengthConflation.swift:114:36:114:46 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | -| StringLengthConflation.swift:119:36:119:38 | .count : | semmle.label | .count : | -| StringLengthConflation.swift:119:36:119:46 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | -| StringLengthConflation.swift:120:38:120:40 | .count : | semmle.label | .count : | -| StringLengthConflation.swift:120:38:120:48 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | -| StringLengthConflation.swift:125:34:125:36 | .count : | semmle.label | .count : | -| StringLengthConflation.swift:125:34:125:44 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | -| StringLengthConflation.swift:126:36:126:38 | .count : | semmle.label | .count : | -| StringLengthConflation.swift:126:36:126:46 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | -| StringLengthConflation.swift:132:28:132:30 | .count : | semmle.label | .count : | -| StringLengthConflation.swift:132:28:132:38 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:122:34:122:36 | .count : | semmle.label | .count : | +| StringLengthConflation.swift:122:34:122:44 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:123:36:123:38 | .count : | semmle.label | .count : | +| StringLengthConflation.swift:123:36:123:46 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:128:36:128:38 | .count : | semmle.label | .count : | +| StringLengthConflation.swift:128:36:128:46 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:129:38:129:40 | .count : | semmle.label | .count : | +| StringLengthConflation.swift:129:38:129:48 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:134:34:134:36 | .count : | semmle.label | .count : | +| StringLengthConflation.swift:134:34:134:44 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:135:36:135:38 | .count : | semmle.label | .count : | +| StringLengthConflation.swift:135:36:135:46 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:141:28:141:30 | .count : | semmle.label | .count : | +| StringLengthConflation.swift:141:28:141:38 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | subpaths #select | StringLengthConflation.swift:72:33:72:35 | .count | StringLengthConflation.swift:72:33:72:35 | .count | StringLengthConflation.swift:72:33:72:35 | .count | This String length is used in an NSString, but it may not be equivalent. | | StringLengthConflation.swift:78:47:78:49 | .count | StringLengthConflation.swift:78:47:78:49 | .count | StringLengthConflation.swift:78:47:78:49 | .count | This String length is used in an NSString, but it may not be equivalent. | -| StringLengthConflation.swift:113:34:113:44 | ... call to -(_:_:) ... | StringLengthConflation.swift:113:34:113:36 | .count : | StringLengthConflation.swift:113:34:113:44 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | -| StringLengthConflation.swift:114:36:114:46 | ... call to -(_:_:) ... | StringLengthConflation.swift:114:36:114:38 | .count : | StringLengthConflation.swift:114:36:114:46 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | -| StringLengthConflation.swift:119:36:119:46 | ... call to -(_:_:) ... | StringLengthConflation.swift:119:36:119:38 | .count : | StringLengthConflation.swift:119:36:119:46 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | -| StringLengthConflation.swift:120:38:120:48 | ... call to -(_:_:) ... | StringLengthConflation.swift:120:38:120:40 | .count : | StringLengthConflation.swift:120:38:120:48 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | -| StringLengthConflation.swift:125:34:125:44 | ... call to -(_:_:) ... | StringLengthConflation.swift:125:34:125:36 | .count : | StringLengthConflation.swift:125:34:125:44 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | -| StringLengthConflation.swift:126:36:126:46 | ... call to -(_:_:) ... | StringLengthConflation.swift:126:36:126:38 | .count : | StringLengthConflation.swift:126:36:126:46 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | -| StringLengthConflation.swift:132:28:132:38 | ... call to -(_:_:) ... | StringLengthConflation.swift:132:28:132:30 | .count : | StringLengthConflation.swift:132:28:132:38 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | +| StringLengthConflation.swift:122:34:122:44 | ... call to -(_:_:) ... | StringLengthConflation.swift:122:34:122:36 | .count : | StringLengthConflation.swift:122:34:122:44 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | +| StringLengthConflation.swift:123:36:123:46 | ... call to -(_:_:) ... | StringLengthConflation.swift:123:36:123:38 | .count : | StringLengthConflation.swift:123:36:123:46 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | +| StringLengthConflation.swift:128:36:128:46 | ... call to -(_:_:) ... | StringLengthConflation.swift:128:36:128:38 | .count : | StringLengthConflation.swift:128:36:128:46 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | +| StringLengthConflation.swift:129:38:129:48 | ... call to -(_:_:) ... | StringLengthConflation.swift:129:38:129:40 | .count : | StringLengthConflation.swift:129:38:129:48 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | +| StringLengthConflation.swift:134:34:134:44 | ... call to -(_:_:) ... | StringLengthConflation.swift:134:34:134:36 | .count : | StringLengthConflation.swift:134:34:134:44 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | +| StringLengthConflation.swift:135:36:135:46 | ... call to -(_:_:) ... | StringLengthConflation.swift:135:36:135:38 | .count : | StringLengthConflation.swift:135:36:135:46 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | +| StringLengthConflation.swift:141:28:141:38 | ... call to -(_:_:) ... | StringLengthConflation.swift:141:28:141:30 | .count : | StringLengthConflation.swift:141:28:141:38 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | diff --git a/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.swift b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.swift index 7a083267aa4..cf248a9a221 100644 --- a/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.swift +++ b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.swift @@ -28,6 +28,7 @@ class NSMutableString : NSString class NSRange { init(location: Int, length: Int) { self.description = "" } + init(_ r: R, in: S) { self.description = "" } private(set) var description: String } @@ -36,7 +37,6 @@ func NSMakeRange(_ loc: Int, _ len: Int) -> NSRange { return NSRange(location: l - // --- tests --- func test(s: String) { @@ -78,6 +78,15 @@ func test(s: String) { let range6 = NSRange(location: 0, length: s.count) // BAD: String length used in NSMakeRange print("NSRange '\(range5.description)' / '\(range6.description)'") + // --- converting Range to NSRange --- + + let range7 = s.startIndex ..< s.endIndex + let range8 = NSRange(range7, in: s) // GOOD + let location = s.distance(from: s.startIndex, to: range7.lowerBound) + let length = s.distance(from: range7.lowerBound, to: range7.upperBound) + let range9 = NSRange(location: location, length: length) // BAD [NOT DETECTED] + print("NSRange '\(range8.description)' / '\(range9.description)'") + // --- String operations using an integer directly --- let str1 = s.dropFirst(s.count - 1) // GOOD From bc03f6959c46c26773679620298fba0188b8efce Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 1 Jul 2022 09:16:05 +0100 Subject: [PATCH 127/505] Swift: Detect String -> NSString results. --- .../CWE-135/StringLengthConflation.ql | 29 +++++++++++++++++++ .../CWE-135/StringLengthConflation.expected | 24 +++++++++++++++ .../CWE-135/StringLengthConflation.swift | 12 ++++---- 3 files changed, 59 insertions(+), 6 deletions(-) diff --git a/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql b/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql index a4e731e18ea..fc62309cdee 100644 --- a/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql +++ b/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql @@ -30,6 +30,14 @@ class StringLengthConflationConfiguration extends DataFlow::Configuration { node.asExpr() = member and flowstate = "String" ) + or + // result of a call to to `NSString.length` + exists(MemberRefExpr member | + member.getBaseExpr().getType().getName() = ["NSString", "NSMutableString"] and + member.getMember().(VarDecl).getName() = "length" and + node.asExpr() = member and + flowstate = "NSString" + ) } override predicate isSink(DataFlow::Node node, string flowstate) { @@ -83,6 +91,27 @@ class StringLengthConflationConfiguration extends DataFlow::Configuration { call.getArgument(pragma[only_bind_into](arg)).getExpr() = node.asExpr() and flowstate = "String" // `String` length flowing into `NSString` ) + or + // arguments to function calls... + exists(string funcName, string paramName, CallExpr call, int arg | + ( + // `dropFirst`, `dropLast`, `removeFirst`, `removeLast` + funcName = ["dropFirst(_:)", "dropLast(_:)", "removeFirst(_:)", "removeLast(_:)"] and + paramName = "k" + or + // `prefix`, `suffix` + funcName = ["prefix(_:)", "suffix(_:)"] and + paramName = "maxLength" + ) and + call.getFunction().(ApplyExpr).getStaticTarget().getName() = funcName and + call.getFunction() + .(ApplyExpr) + .getStaticTarget() + .getParam(pragma[only_bind_into](arg)) + .getName() = paramName and + call.getArgument(pragma[only_bind_into](arg)).getExpr() = node.asExpr() and + flowstate = "NSString" // `NSString` length flowing into `String` + ) } override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { diff --git a/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.expected b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.expected index 2654d28d334..a68894b7d9c 100644 --- a/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.expected +++ b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.expected @@ -1,4 +1,10 @@ edges +| StringLengthConflation.swift:93:28:93:31 | .length : | StringLengthConflation.swift:93:28:93:40 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:97:27:97:30 | .length : | StringLengthConflation.swift:97:27:97:39 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:101:25:101:28 | .length : | StringLengthConflation.swift:101:25:101:37 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:105:25:105:28 | .length : | StringLengthConflation.swift:105:25:105:37 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:111:23:111:26 | .length : | StringLengthConflation.swift:111:23:111:35 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:117:22:117:25 | .length : | StringLengthConflation.swift:117:22:117:34 | ... call to -(_:_:) ... | | StringLengthConflation.swift:122:34:122:36 | .count : | StringLengthConflation.swift:122:34:122:44 | ... call to -(_:_:) ... | | StringLengthConflation.swift:123:36:123:38 | .count : | StringLengthConflation.swift:123:36:123:46 | ... call to -(_:_:) ... | | StringLengthConflation.swift:128:36:128:38 | .count : | StringLengthConflation.swift:128:36:128:46 | ... call to -(_:_:) ... | @@ -9,6 +15,18 @@ edges nodes | StringLengthConflation.swift:72:33:72:35 | .count | semmle.label | .count | | StringLengthConflation.swift:78:47:78:49 | .count | semmle.label | .count | +| StringLengthConflation.swift:93:28:93:31 | .length : | semmle.label | .length : | +| StringLengthConflation.swift:93:28:93:40 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:97:27:97:30 | .length : | semmle.label | .length : | +| StringLengthConflation.swift:97:27:97:39 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:101:25:101:28 | .length : | semmle.label | .length : | +| StringLengthConflation.swift:101:25:101:37 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:105:25:105:28 | .length : | semmle.label | .length : | +| StringLengthConflation.swift:105:25:105:37 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:111:23:111:26 | .length : | semmle.label | .length : | +| StringLengthConflation.swift:111:23:111:35 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:117:22:117:25 | .length : | semmle.label | .length : | +| StringLengthConflation.swift:117:22:117:34 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | | StringLengthConflation.swift:122:34:122:36 | .count : | semmle.label | .count : | | StringLengthConflation.swift:122:34:122:44 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | | StringLengthConflation.swift:123:36:123:38 | .count : | semmle.label | .count : | @@ -27,6 +45,12 @@ subpaths #select | StringLengthConflation.swift:72:33:72:35 | .count | StringLengthConflation.swift:72:33:72:35 | .count | StringLengthConflation.swift:72:33:72:35 | .count | This String length is used in an NSString, but it may not be equivalent. | | StringLengthConflation.swift:78:47:78:49 | .count | StringLengthConflation.swift:78:47:78:49 | .count | StringLengthConflation.swift:78:47:78:49 | .count | This String length is used in an NSString, but it may not be equivalent. | +| StringLengthConflation.swift:93:28:93:40 | ... call to -(_:_:) ... | StringLengthConflation.swift:93:28:93:31 | .length : | StringLengthConflation.swift:93:28:93:40 | ... call to -(_:_:) ... | This NSString length is used in a String, but it may not be equivalent. | +| StringLengthConflation.swift:97:27:97:39 | ... call to -(_:_:) ... | StringLengthConflation.swift:97:27:97:30 | .length : | StringLengthConflation.swift:97:27:97:39 | ... call to -(_:_:) ... | This NSString length is used in a String, but it may not be equivalent. | +| StringLengthConflation.swift:101:25:101:37 | ... call to -(_:_:) ... | StringLengthConflation.swift:101:25:101:28 | .length : | StringLengthConflation.swift:101:25:101:37 | ... call to -(_:_:) ... | This NSString length is used in a String, but it may not be equivalent. | +| StringLengthConflation.swift:105:25:105:37 | ... call to -(_:_:) ... | StringLengthConflation.swift:105:25:105:28 | .length : | StringLengthConflation.swift:105:25:105:37 | ... call to -(_:_:) ... | This NSString length is used in a String, but it may not be equivalent. | +| StringLengthConflation.swift:111:23:111:35 | ... call to -(_:_:) ... | StringLengthConflation.swift:111:23:111:26 | .length : | StringLengthConflation.swift:111:23:111:35 | ... call to -(_:_:) ... | This NSString length is used in a String, but it may not be equivalent. | +| StringLengthConflation.swift:117:22:117:34 | ... call to -(_:_:) ... | StringLengthConflation.swift:117:22:117:25 | .length : | StringLengthConflation.swift:117:22:117:34 | ... call to -(_:_:) ... | This NSString length is used in a String, but it may not be equivalent. | | StringLengthConflation.swift:122:34:122:44 | ... call to -(_:_:) ... | StringLengthConflation.swift:122:34:122:36 | .count : | StringLengthConflation.swift:122:34:122:44 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | | StringLengthConflation.swift:123:36:123:46 | ... call to -(_:_:) ... | StringLengthConflation.swift:123:36:123:38 | .count : | StringLengthConflation.swift:123:36:123:46 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | | StringLengthConflation.swift:128:36:128:46 | ... call to -(_:_:) ... | StringLengthConflation.swift:128:36:128:38 | .count : | StringLengthConflation.swift:128:36:128:46 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | diff --git a/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.swift b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.swift index cf248a9a221..60037ffd676 100644 --- a/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.swift +++ b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.swift @@ -90,31 +90,31 @@ func test(s: String) { // --- String operations using an integer directly --- let str1 = s.dropFirst(s.count - 1) // GOOD - let str2 = s.dropFirst(ns.length - 1) // BAD: NSString length used in String [NOT DETECTED] + let str2 = s.dropFirst(ns.length - 1) // BAD: NSString length used in String print("dropFirst '\(str1)' / '\(str2)'") let str3 = s.dropLast(s.count - 1) // GOOD - let str4 = s.dropLast(ns.length - 1) // BAD: NSString length used in String [NOT DETECTED] + let str4 = s.dropLast(ns.length - 1) // BAD: NSString length used in String print("dropLast '\(str3)' / '\(str4)'") let str5 = s.prefix(s.count - 1) // GOOD - let str6 = s.prefix(ns.length - 1) // BAD: NSString length used in String [NOT DETECTED] + let str6 = s.prefix(ns.length - 1) // BAD: NSString length used in String print("prefix '\(str5)' / '\(str6)'") let str7 = s.suffix(s.count - 1) // GOOD - let str8 = s.suffix(ns.length - 1) // BAD: NSString length used in String [NOT DETECTED] + let str8 = s.suffix(ns.length - 1) // BAD: NSString length used in String print("suffix '\(str7)' / '\(str8)'") var str9 = s str9.removeFirst(s.count - 1) // GOOD var str10 = s - str10.removeFirst(ns.length - 1) // BAD: NSString length used in String [NOT DETECTED] + str10.removeFirst(ns.length - 1) // BAD: NSString length used in String print("removeFirst '\(str9)' / '\(str10)'") var str11 = s str11.removeLast(s.count - 1) // GOOD var str12 = s - str12.removeLast(ns.length - 1) // BAD: NSString length used in String [NOT DETECTED] + str12.removeLast(ns.length - 1) // BAD: NSString length used in String print("removeLast '\(str11)' / '\(str12)'") let nstr1 = ns.character(at: ns.length - 1) // GOOD From d60d2457c215def7b2e838c2aee0b44c7dc816e5 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 1 Jul 2022 13:33:25 +0100 Subject: [PATCH 128/505] Swift: Add String.Index.init as a source as as well. --- .../ql/src/queries/Security/CWE-135/StringLengthConflation.ql | 4 ++++ .../Security/CWE-135/StringLengthConflation.expected | 2 ++ .../query-tests/Security/CWE-135/StringLengthConflation.swift | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql b/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql index fc62309cdee..38485aa119d 100644 --- a/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql +++ b/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql @@ -102,6 +102,10 @@ class StringLengthConflationConfiguration extends DataFlow::Configuration { // `prefix`, `suffix` funcName = ["prefix(_:)", "suffix(_:)"] and paramName = "maxLength" + or + // `String.Index.init` + funcName = "init(encodedOffset:)" and + paramName = "offset" ) and call.getFunction().(ApplyExpr).getStaticTarget().getName() = funcName and call.getFunction() diff --git a/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.expected b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.expected index a68894b7d9c..411a938ba0e 100644 --- a/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.expected +++ b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.expected @@ -13,6 +13,7 @@ edges | StringLengthConflation.swift:135:36:135:38 | .count : | StringLengthConflation.swift:135:36:135:46 | ... call to -(_:_:) ... | | StringLengthConflation.swift:141:28:141:30 | .count : | StringLengthConflation.swift:141:28:141:38 | ... call to -(_:_:) ... | nodes +| StringLengthConflation.swift:53:43:53:46 | .length | semmle.label | .length | | StringLengthConflation.swift:72:33:72:35 | .count | semmle.label | .count | | StringLengthConflation.swift:78:47:78:49 | .count | semmle.label | .count | | StringLengthConflation.swift:93:28:93:31 | .length : | semmle.label | .length : | @@ -43,6 +44,7 @@ nodes | StringLengthConflation.swift:141:28:141:38 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | subpaths #select +| StringLengthConflation.swift:53:43:53:46 | .length | StringLengthConflation.swift:53:43:53:46 | .length | StringLengthConflation.swift:53:43:53:46 | .length | This NSString length is used in a String, but it may not be equivalent. | | StringLengthConflation.swift:72:33:72:35 | .count | StringLengthConflation.swift:72:33:72:35 | .count | StringLengthConflation.swift:72:33:72:35 | .count | This String length is used in an NSString, but it may not be equivalent. | | StringLengthConflation.swift:78:47:78:49 | .count | StringLengthConflation.swift:78:47:78:49 | .count | StringLengthConflation.swift:78:47:78:49 | .count | This String length is used in an NSString, but it may not be equivalent. | | StringLengthConflation.swift:93:28:93:40 | ... call to -(_:_:) ... | StringLengthConflation.swift:93:28:93:31 | .length : | StringLengthConflation.swift:93:28:93:40 | ... call to -(_:_:) ... | This NSString length is used in a String, but it may not be equivalent. | diff --git a/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.swift b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.swift index 60037ffd676..87985d6c8fe 100644 --- a/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.swift +++ b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.swift @@ -50,7 +50,7 @@ func test(s: String) { // --- constructing a String.Index from integer --- let ix1 = String.Index(encodedOffset: s.count) // GOOD - let ix2 = String.Index(encodedOffset: ns.length) // BAD: NSString length used in String.Index [NOT DETECTED] + let ix2 = String.Index(encodedOffset: ns.length) // BAD: NSString length used in String.Index let ix3 = String.Index(encodedOffset: s.utf8.count) // BAD: String.utf8 length used in String.Index [NOT DETECTED] let ix4 = String.Index(encodedOffset: s.utf16.count) // BAD: String.utf16 length used in String.Index [NOT DETECTED] let ix5 = String.Index(encodedOffset: s.unicodeScalars.count) // BAD: string.unicodeScalars length used in String.Index [NOT DETECTED] From 34ffd1aac58267468ac10ffdb22f0a7f8502fead Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 1 Jul 2022 13:43:37 +0100 Subject: [PATCH 129/505] Swift: Support String.Index and flow through * /. --- .../Security/CWE-135/StringLengthConflation.ql | 17 ++++++++++++----- .../CWE-135/StringLengthConflation.expected | 8 ++++++++ .../CWE-135/StringLengthConflation.swift | 4 ++-- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql b/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql index 38485aa119d..59146dfba7a 100644 --- a/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql +++ b/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql @@ -95,17 +95,25 @@ class StringLengthConflationConfiguration extends DataFlow::Configuration { // arguments to function calls... exists(string funcName, string paramName, CallExpr call, int arg | ( - // `dropFirst`, `dropLast`, `removeFirst`, `removeLast` + // `String.dropFirst`, `String.dropLast`, `String.removeFirst`, `String.removeLast` funcName = ["dropFirst(_:)", "dropLast(_:)", "removeFirst(_:)", "removeLast(_:)"] and paramName = "k" or - // `prefix`, `suffix` + // `String.prefix`, `String.suffix` funcName = ["prefix(_:)", "suffix(_:)"] and paramName = "maxLength" or // `String.Index.init` funcName = "init(encodedOffset:)" and paramName = "offset" + or + // `String.index` + funcName = ["index(_:offsetBy:)", "index(_:offsetBy:limitBy:)"] and + paramName = "n" + or + // `String.formIndex` + funcName = ["formIndex(_:offsetBy:)", "formIndex(_:offsetBy:limitBy:)"] and + paramName = "distance" ) and call.getFunction().(ApplyExpr).getStaticTarget().getName() = funcName and call.getFunction() @@ -119,9 +127,8 @@ class StringLengthConflationConfiguration extends DataFlow::Configuration { } override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { - // allow flow through `+` and `-`. - node2.asExpr().(AddExpr).getAnOperand() = node1.asExpr() or - node2.asExpr().(SubExpr).getAnOperand() = node1.asExpr() + // allow flow through `+`, `-`, `*` etc. + node2.asExpr().(ArithmeticOperation).getAnOperand() = node1.asExpr() } } diff --git a/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.expected b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.expected index 411a938ba0e..1af416dad2f 100644 --- a/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.expected +++ b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.expected @@ -1,4 +1,6 @@ edges +| StringLengthConflation.swift:60:47:60:50 | .length : | StringLengthConflation.swift:60:47:60:59 | ... call to /(_:_:) ... | +| StringLengthConflation.swift:66:33:66:36 | .length : | StringLengthConflation.swift:66:33:66:45 | ... call to /(_:_:) ... | | StringLengthConflation.swift:93:28:93:31 | .length : | StringLengthConflation.swift:93:28:93:40 | ... call to -(_:_:) ... | | StringLengthConflation.swift:97:27:97:30 | .length : | StringLengthConflation.swift:97:27:97:39 | ... call to -(_:_:) ... | | StringLengthConflation.swift:101:25:101:28 | .length : | StringLengthConflation.swift:101:25:101:37 | ... call to -(_:_:) ... | @@ -14,6 +16,10 @@ edges | StringLengthConflation.swift:141:28:141:30 | .count : | StringLengthConflation.swift:141:28:141:38 | ... call to -(_:_:) ... | nodes | StringLengthConflation.swift:53:43:53:46 | .length | semmle.label | .length | +| StringLengthConflation.swift:60:47:60:50 | .length : | semmle.label | .length : | +| StringLengthConflation.swift:60:47:60:59 | ... call to /(_:_:) ... | semmle.label | ... call to /(_:_:) ... | +| StringLengthConflation.swift:66:33:66:36 | .length : | semmle.label | .length : | +| StringLengthConflation.swift:66:33:66:45 | ... call to /(_:_:) ... | semmle.label | ... call to /(_:_:) ... | | StringLengthConflation.swift:72:33:72:35 | .count | semmle.label | .count | | StringLengthConflation.swift:78:47:78:49 | .count | semmle.label | .count | | StringLengthConflation.swift:93:28:93:31 | .length : | semmle.label | .length : | @@ -45,6 +51,8 @@ nodes subpaths #select | StringLengthConflation.swift:53:43:53:46 | .length | StringLengthConflation.swift:53:43:53:46 | .length | StringLengthConflation.swift:53:43:53:46 | .length | This NSString length is used in a String, but it may not be equivalent. | +| StringLengthConflation.swift:60:47:60:59 | ... call to /(_:_:) ... | StringLengthConflation.swift:60:47:60:50 | .length : | StringLengthConflation.swift:60:47:60:59 | ... call to /(_:_:) ... | This NSString length is used in a String, but it may not be equivalent. | +| StringLengthConflation.swift:66:33:66:45 | ... call to /(_:_:) ... | StringLengthConflation.swift:66:33:66:36 | .length : | StringLengthConflation.swift:66:33:66:45 | ... call to /(_:_:) ... | This NSString length is used in a String, but it may not be equivalent. | | StringLengthConflation.swift:72:33:72:35 | .count | StringLengthConflation.swift:72:33:72:35 | .count | StringLengthConflation.swift:72:33:72:35 | .count | This String length is used in an NSString, but it may not be equivalent. | | StringLengthConflation.swift:78:47:78:49 | .count | StringLengthConflation.swift:78:47:78:49 | .count | StringLengthConflation.swift:78:47:78:49 | .count | This String length is used in an NSString, but it may not be equivalent. | | StringLengthConflation.swift:93:28:93:40 | ... call to -(_:_:) ... | StringLengthConflation.swift:93:28:93:31 | .length : | StringLengthConflation.swift:93:28:93:40 | ... call to -(_:_:) ... | This NSString length is used in a String, but it may not be equivalent. | diff --git a/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.swift b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.swift index 87985d6c8fe..527cacd772b 100644 --- a/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.swift +++ b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.swift @@ -57,13 +57,13 @@ func test(s: String) { print("String.Index '\(ix1.encodedOffset)' / '\(ix2.encodedOffset)' '\(ix3.encodedOffset)' '\(ix4.encodedOffset)' '\(ix5.encodedOffset)'") let ix6 = s.index(s.startIndex, offsetBy: s.count / 2) // GOOD - let ix7 = s.index(s.startIndex, offsetBy: ns.length / 2) // BAD: NSString length used in String.Index [NOT DETECTED] + let ix7 = s.index(s.startIndex, offsetBy: ns.length / 2) // BAD: NSString length used in String.Index print("index '\(ix6.encodedOffset)' / '\(ix7.encodedOffset)'") var ix8 = s.startIndex s.formIndex(&ix8, offsetBy: s.count / 2) // GOOD var ix9 = s.startIndex - s.formIndex(&ix9, offsetBy: ns.length / 2) // BAD: NSString length used in String.Index [NOT DETECTED] + s.formIndex(&ix9, offsetBy: ns.length / 2) // BAD: NSString length used in String.Index print("formIndex '\(ix8.encodedOffset)' / '\(ix9.encodedOffset)'") // --- constructing an NSRange from integers --- From e88cc314681ee626a8603a5d2c53321a26372cb3 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Fri, 1 Jul 2022 16:16:21 +0200 Subject: [PATCH 130/505] Swift: disable change note checking for now --- .github/workflows/check-change-note.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/check-change-note.yml b/.github/workflows/check-change-note.yml index 672202444bb..b60a590ab09 100644 --- a/.github/workflows/check-change-note.yml +++ b/.github/workflows/check-change-note.yml @@ -10,6 +10,7 @@ on: - "*/ql/lib/**/*.qll" - "!**/experimental/**" - "!ql/**" + - "!swift/**" - ".github/workflows/check-change-note.yml" jobs: From 2dca78295d5b3cd68387e8258fd32c69b5c8c418 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Fri, 1 Jul 2022 16:35:30 +0200 Subject: [PATCH 131/505] Fix change note check to accept changes to itself The file is not removed from the triggers, as we still want to check that the workflow file itself is correct. --- .github/workflows/check-change-note.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/check-change-note.yml b/.github/workflows/check-change-note.yml index b60a590ab09..8a450474e06 100644 --- a/.github/workflows/check-change-note.yml +++ b/.github/workflows/check-change-note.yml @@ -24,5 +24,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - gh api 'repos/${{github.repository}}/pulls/${{github.event.number}}/files' --paginate --jq 'any(.[].filename ; test("/change-notes/.*[.]md$"))' | + gh api 'repos/${{github.repository}}/pulls/${{github.event.number}}/files' --paginate --jq \ + 'any(.[].filename ; test("/change-notes/.*[.]md$")) or + all(.[].filename ; .==".github/workflows/check-change-note.yml")' | grep true -c From c393c9b03e9e747e0cc57979b8e56012fafd12fa Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Fri, 1 Jul 2022 16:41:09 +0200 Subject: [PATCH 132/505] Revert "Fix change note check to accept changes to itself" This reverts commit 2dca78295d5b3cd68387e8258fd32c69b5c8c418. --- .github/workflows/check-change-note.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/check-change-note.yml b/.github/workflows/check-change-note.yml index 8a450474e06..b60a590ab09 100644 --- a/.github/workflows/check-change-note.yml +++ b/.github/workflows/check-change-note.yml @@ -24,7 +24,5 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - gh api 'repos/${{github.repository}}/pulls/${{github.event.number}}/files' --paginate --jq \ - 'any(.[].filename ; test("/change-notes/.*[.]md$")) or - all(.[].filename ; .==".github/workflows/check-change-note.yml")' | + gh api 'repos/${{github.repository}}/pulls/${{github.event.number}}/files' --paginate --jq 'any(.[].filename ; test("/change-notes/.*[.]md$"))' | grep true -c From b499ba5aa8b97478b12041fe1882ec7fc3c5d6c4 Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Fri, 1 Jul 2022 15:35:32 +0100 Subject: [PATCH 133/505] Kotlin: don't extract private setters of external classes Previously these would get extracted unlike other private methods even if the class was a standard library or other external class. This could cause inconsistencies because if we also compiled the class from source we could end up deciding different names for the property's setter: setXyz$private when seen from source, and setXyz without a suffix when seen as an external .class file. Avoiding extracting these functions from the external perspective both restores consistency with other kinds of method and avoids these consistency problems. --- .../src/main/kotlin/KotlinFileExtractor.kt | 26 +++++++++---------- .../private_property_accessors/hasprops.kt | 9 +++++++ .../private_property_accessors/test.expected | 7 +++++ .../kotlin/private_property_accessors/test.py | 3 +++ .../kotlin/private_property_accessors/test.ql | 5 ++++ .../private_property_accessors/usesprops.kt | 9 +++++++ 6 files changed, 46 insertions(+), 13 deletions(-) create mode 100644 java/ql/integration-tests/posix-only/kotlin/private_property_accessors/hasprops.kt create mode 100644 java/ql/integration-tests/posix-only/kotlin/private_property_accessors/test.expected create mode 100644 java/ql/integration-tests/posix-only/kotlin/private_property_accessors/test.py create mode 100644 java/ql/integration-tests/posix-only/kotlin/private_property_accessors/test.ql create mode 100644 java/ql/integration-tests/posix-only/kotlin/private_property_accessors/usesprops.kt diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt index 21b35fbcdd1..9bdf40fdca0 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt @@ -134,7 +134,7 @@ open class KotlinFileExtractor( is IrProperty -> { val parentId = useDeclarationParent(declaration.parent, false)?.cast() if (parentId != null) { - extractProperty(declaration, parentId, extractBackingField = true, extractFunctionBodies = extractFunctionBodies, null, listOf()) + extractProperty(declaration, parentId, extractBackingField = true, extractFunctionBodies = extractFunctionBodies, extractPrivateMembers = extractPrivateMembers, null, listOf()) } Unit } @@ -364,7 +364,7 @@ open class KotlinFileExtractor( if (shouldExtractDecl(it, false)) { when(it) { is IrFunction -> extractFunction(it, id, extractBody = false, extractMethodAndParameterTypeAccesses = false, typeParamSubstitution, argsIncludingOuterClasses) - is IrProperty -> extractProperty(it, id, extractBackingField = false, extractFunctionBodies = false, typeParamSubstitution, argsIncludingOuterClasses) + is IrProperty -> extractProperty(it, id, extractBackingField = false, extractFunctionBodies = false, extractPrivateMembers = false, typeParamSubstitution, argsIncludingOuterClasses) else -> {} } } @@ -955,7 +955,7 @@ open class KotlinFileExtractor( return id } - private fun extractProperty(p: IrProperty, parentId: Label, extractBackingField: Boolean, extractFunctionBodies: Boolean, typeSubstitution: TypeSubstitution?, classTypeArgsIncludingOuterClasses: List?) { + private fun extractProperty(p: IrProperty, parentId: Label, extractBackingField: Boolean, extractFunctionBodies: Boolean, extractPrivateMembers: Boolean, typeSubstitution: TypeSubstitution?, classTypeArgsIncludingOuterClasses: List?) { with("property", p) { if (isFake(p)) return @@ -970,7 +970,11 @@ open class KotlinFileExtractor( val getter = p.getter val setter = p.setter - if (getter != null) { + if (getter == null) { + if (p.modality != Modality.FINAL || !isExternalDeclaration(p)) { + logger.warnElement("IrProperty without a getter", p) + } + } else if (shouldExtractDecl(getter, extractPrivateMembers)) { val getterId = extractFunction(getter, parentId, extractBody = extractFunctionBodies, extractMethodAndParameterTypeAccesses = extractFunctionBodies, typeSubstitution, classTypeArgsIncludingOuterClasses)?.cast() if (getterId != null) { tw.writeKtPropertyGetters(id, getterId) @@ -978,13 +982,13 @@ open class KotlinFileExtractor( tw.writeCompiler_generated(getterId, CompilerGeneratedKinds.DELEGATED_PROPERTY_GETTER.kind) } } - } else { - if (p.modality != Modality.FINAL || !isExternalDeclaration(p)) { - logger.warnElement("IrProperty without a getter", p) - } } - if (setter != null) { + if (setter == null) { + if (p.isVar && !isExternalDeclaration(p)) { + logger.warnElement("isVar property without a setter", p) + } + } else if (shouldExtractDecl(setter, extractPrivateMembers)) { if (!p.isVar) { logger.warnElement("!isVar property with a setter", p) } @@ -995,10 +999,6 @@ open class KotlinFileExtractor( tw.writeCompiler_generated(setterId, CompilerGeneratedKinds.DELEGATED_PROPERTY_SETTER.kind) } } - } else { - if (p.isVar && !isExternalDeclaration(p)) { - logger.warnElement("isVar property without a setter", p) - } } if (bf != null && extractBackingField) { diff --git a/java/ql/integration-tests/posix-only/kotlin/private_property_accessors/hasprops.kt b/java/ql/integration-tests/posix-only/kotlin/private_property_accessors/hasprops.kt new file mode 100644 index 00000000000..ff83b18a31a --- /dev/null +++ b/java/ql/integration-tests/posix-only/kotlin/private_property_accessors/hasprops.kt @@ -0,0 +1,9 @@ +class HasProps { + + var accessorsPublic = 1 + + var setterPrivate = 3 + private set + +} + diff --git a/java/ql/integration-tests/posix-only/kotlin/private_property_accessors/test.expected b/java/ql/integration-tests/posix-only/kotlin/private_property_accessors/test.expected new file mode 100644 index 00000000000..c497684b152 --- /dev/null +++ b/java/ql/integration-tests/posix-only/kotlin/private_property_accessors/test.expected @@ -0,0 +1,7 @@ +| hasprops.kt:3:3:3:25 | getAccessorsPublic | +| hasprops.kt:3:3:3:25 | setAccessorsPublic | +| hasprops.kt:5:3:6:15 | getSetterPrivate | +| hasprops.kt:6:13:6:15 | setSetterPrivate$private | +| usesprops.kt:1:1:9:1 | user | +| usesprops.kt:3:3:3:58 | useGetters | +| usesprops.kt:5:3:7:3 | useSetter | diff --git a/java/ql/integration-tests/posix-only/kotlin/private_property_accessors/test.py b/java/ql/integration-tests/posix-only/kotlin/private_property_accessors/test.py new file mode 100644 index 00000000000..5599f459849 --- /dev/null +++ b/java/ql/integration-tests/posix-only/kotlin/private_property_accessors/test.py @@ -0,0 +1,3 @@ +from create_database_utils import * + +run_codeql_database_create(["kotlinc hasprops.kt", "kotlinc usesprops.kt -cp ."], lang="java") diff --git a/java/ql/integration-tests/posix-only/kotlin/private_property_accessors/test.ql b/java/ql/integration-tests/posix-only/kotlin/private_property_accessors/test.ql new file mode 100644 index 00000000000..f1355df2e88 --- /dev/null +++ b/java/ql/integration-tests/posix-only/kotlin/private_property_accessors/test.ql @@ -0,0 +1,5 @@ +import java + +from Method m +where m.fromSource() +select m diff --git a/java/ql/integration-tests/posix-only/kotlin/private_property_accessors/usesprops.kt b/java/ql/integration-tests/posix-only/kotlin/private_property_accessors/usesprops.kt new file mode 100644 index 00000000000..5157865df17 --- /dev/null +++ b/java/ql/integration-tests/posix-only/kotlin/private_property_accessors/usesprops.kt @@ -0,0 +1,9 @@ +fun user(hp: HasProps) { + + fun useGetters() = hp.accessorsPublic + hp.setterPrivate + + fun useSetter(x: Int) { + hp.accessorsPublic = x + } + +} From 1730ec22d92cd1dfd0bcf57590c85240cdd341af Mon Sep 17 00:00:00 2001 From: Ian Lynagh Date: Thu, 30 Jun 2022 15:34:45 +0100 Subject: [PATCH 134/505] Kotlin: Extract an ErrorType if we fail to correctly extract a type --- .../src/main/kotlin/KotlinUsesExtractor.kt | 18 +++++++++++++++--- java/ql/lib/config/semmlecode.dbscheme | 12 ++++++++++-- java/ql/lib/semmle/code/Location.qll | 2 ++ java/ql/lib/semmle/code/java/Type.qll | 8 ++++++++ 4 files changed, 35 insertions(+), 5 deletions(-) diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt index 530ff47e8ab..bf3056b079b 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt @@ -108,14 +108,26 @@ open class KotlinUsesExtractor( } data class TypeResults(val javaResult: TypeResult, val kotlinResult: TypeResult) - fun useType(t: IrType, context: TypeContext = TypeContext.OTHER) = + fun useType(t: IrType, context: TypeContext = TypeContext.OTHER): TypeResults { when(t) { - is IrSimpleType -> useSimpleType(t, context) + is IrSimpleType -> return useSimpleType(t, context) else -> { logger.error("Unrecognised IrType: " + t.javaClass) - TypeResults(TypeResult(fakeLabel(), "unknown", "unknown"), TypeResult(fakeLabel(), "unknown", "unknown")) + return extractErrorType() } } + } + + private fun extractErrorType(): TypeResults { + val typeId = tw.getLabelFor("@\"errorType\"") { + tw.writeError_type(it) + } + val kotlinTypeId = tw.getLabelFor("@\"errorKotlinType\"") { + tw.writeKt_nullable_types(it, typeId) + } + return TypeResults(TypeResult(typeId, null, ""), + TypeResult(kotlinTypeId, null, "")) + } fun getJavaEquivalentClass(c: IrClass) = getJavaEquivalentClassId(c)?.let { pluginContext.referenceClass(it.asSingleFqName()) }?.owner diff --git a/java/ql/lib/config/semmlecode.dbscheme b/java/ql/lib/config/semmlecode.dbscheme index cf58c7d9b1f..81ccfabe82e 100755 --- a/java/ql/lib/config/semmlecode.dbscheme +++ b/java/ql/lib/config/semmlecode.dbscheme @@ -332,6 +332,14 @@ modifiers( string nodeName: string ref ); +/** + * An errortype is used when the extractor is unable to extract a type + * correctly for some reason. + */ +error_type( + unique int id: @errortype +); + classes( unique int id: @class, string nodeName: string ref, @@ -1012,13 +1020,13 @@ javadocText( @classorinterfaceorpackage = @classorinterface | @package; @classorinterfaceorcallable = @classorinterface | @callable; @boundedtype = @typevariable | @wildcard; -@reftype = @classorinterface | @array | @boundedtype; +@reftype = @classorinterface | @array | @boundedtype | @errortype; @classorarray = @class | @array; @type = @primitive | @reftype; @callable = @method | @constructor; /** A program element that has a name. */ -@element = @package | @modifier | @annotation | +@element = @package | @modifier | @annotation | @errortype | @locatableElement; @locatableElement = @file | @primitive | @class | @interface | @method | @constructor | @param | @exception | @field | diff --git a/java/ql/lib/semmle/code/Location.qll b/java/ql/lib/semmle/code/Location.qll index d977395a83c..8bb81becb11 100755 --- a/java/ql/lib/semmle/code/Location.qll +++ b/java/ql/lib/semmle/code/Location.qll @@ -47,6 +47,8 @@ predicate hasName(Element e, string name) { kt_type_alias(e, name, _) or ktProperties(e, name) + or + e instanceof ErrorType and name = "" } /** diff --git a/java/ql/lib/semmle/code/java/Type.qll b/java/ql/lib/semmle/code/java/Type.qll index a37f9810c44..2193720c8fe 100755 --- a/java/ql/lib/semmle/code/java/Type.qll +++ b/java/ql/lib/semmle/code/java/Type.qll @@ -666,6 +666,14 @@ class RefType extends Type, Annotatable, Modifiable, @reftype { } } +/** + * An `ErrorType` is generated when CodeQL is unable to correctly + * extract a type. + */ +class ErrorType extends RefType, @errortype { + override string getAPrimaryQlClass() { result = "ErrorType" } +} + /** A type that is the same as its source declaration. */ class SrcRefType extends RefType { SrcRefType() { this.isSourceDeclaration() } From e38254c05e1f32916e55c0e3e48aebf9a70ffc02 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 1 Jul 2022 17:00:36 +0100 Subject: [PATCH 135/505] Swift: Fix typo. --- .../ql/src/queries/Security/CWE-135/StringLengthConflation.ql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql b/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql index 59146dfba7a..398c48f01e5 100644 --- a/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql +++ b/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql @@ -23,7 +23,7 @@ class StringLengthConflationConfiguration extends DataFlow::Configuration { StringLengthConflationConfiguration() { this = "StringLengthConflationConfiguration" } override predicate isSource(DataFlow::Node node, string flowstate) { - // result of a call to to `String.count` + // result of a call to `String.count` exists(MemberRefExpr member | member.getBaseExpr().getType().getName() = "String" and member.getMember().(VarDecl).getName() = "count" and @@ -31,7 +31,7 @@ class StringLengthConflationConfiguration extends DataFlow::Configuration { flowstate = "String" ) or - // result of a call to to `NSString.length` + // result of a call to `NSString.length` exists(MemberRefExpr member | member.getBaseExpr().getType().getName() = ["NSString", "NSMutableString"] and member.getMember().(VarDecl).getName() = "length" and From e43e5810cfcff8e2d333473a43604d7b2d997396 Mon Sep 17 00:00:00 2001 From: Raul Garcia Date: Fri, 1 Jul 2022 17:08:35 -0700 Subject: [PATCH 136/505] New queries to detect unsafe client side encryption in Azure Storage --- ...nsafeUsageOfClientSideEncryptionVersion.cs | 33 ++++++++ ...feUsageOfClientSideEncryptionVersion.qhelp | 31 ++++++++ ...nsafeUsageOfClientSideEncryptionVersion.ql | 75 +++++++++++++++++++ ...afeUsageOfClientSideEncryptionVersion.java | 46 ++++++++++++ ...feUsageOfClientSideEncryptionVersion.qhelp | 31 ++++++++ ...nsafeUsageOfClientSideEncryptionVersion.ql | 71 ++++++++++++++++++ ...nsafeUsageOfClientSideEncryptionVersion.py | 7 ++ ...feUsageOfClientSideEncryptionVersion.qhelp | 31 ++++++++ ...nsafeUsageOfClientSideEncryptionVersion.ql | 55 ++++++++++++++ 9 files changed, 380 insertions(+) create mode 100644 csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.cs create mode 100644 csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp create mode 100644 csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql create mode 100644 java/ql/src/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.java create mode 100644 java/ql/src/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp create mode 100644 java/ql/src/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql create mode 100644 python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.py create mode 100644 python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp create mode 100644 python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql diff --git a/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.cs b/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.cs new file mode 100644 index 00000000000..39928d9e2c7 --- /dev/null +++ b/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.cs @@ -0,0 +1,33 @@ + +var client = new BlobClient(myConnectionString, new SpecializedBlobClientOptions() +{ + // BAD: Using an outdated SDK that does not support client side encryption version V2_0 + ClientSideEncryption = new ClientSideEncryptionOptions() + { + KeyEncryptionKey = myKey, + KeyResolver = myKeyResolver, + KeyWrapAlgorihm = myKeyWrapAlgorithm + } +}); + +var client = new BlobClient(myConnectionString, new SpecializedBlobClientOptions() +{ + // BAD: Using the outdated client side encryption version V1_0 + ClientSideEncryption = new ClientSideEncryptionOptions(ClientSideEncryptionVersion.V1_0) + { + KeyEncryptionKey = myKey, + KeyResolver = myKeyResolver, + KeyWrapAlgorihm = myKeyWrapAlgorithm + } +}); + +var client = new BlobClient(myConnectionString, new SpecializedBlobClientOptions() +{ + // GOOD: Using client side encryption version V2_0 + ClientSideEncryption = new ClientSideEncryptionOptions(ClientSideEncryptionVersion.V2_0) + { + KeyEncryptionKey = myKey, + KeyResolver = myKeyResolver, + KeyWrapAlgorihm = myKeyWrapAlgorithm + } +}); \ No newline at end of file diff --git a/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp b/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp new file mode 100644 index 00000000000..3bf46ab9f89 --- /dev/null +++ b/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp @@ -0,0 +1,31 @@ + + + + + +

    Azure Storage .NET, Java, and Python SDKs support encryption on the client with a customer-managed key that is maintained in Azure Key Vault or another key store.

    +

    Current release versions of the Azure Storage SDKs use cipher block chaining (CBC mode) for client-side encryption (referred to as v1).

    + +
    + + +

    Consider switching to v1 client-side encryption.

    + +
    + + +

    The following example shows an HTTP request parameter being used directly in a forming a +new request without validating the input, which facilitates SSRF attacks. +It also shows how to remedy the problem by validating the user input against a known fixed string. +

    + + + +
    + +
  • + Azure Storage Client Encryption Blog. +
  • + +
    +
    diff --git a/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql b/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql new file mode 100644 index 00000000000..cb986754958 --- /dev/null +++ b/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql @@ -0,0 +1,75 @@ +/** + * @name Unsafe usage of v1 version of Azure Storage client-side encryption (CVE-2022-PENDING). + * @description Unsafe usage of v1 version of Azure Storage client-side encryption, please refer to http://aka.ms/azstorageclientencryptionblog + * @kind problem + * @tags security + * cryptography + * external/cwe/cwe-327 + * @id cs/azure-storage/unsafe-usage-of-client-side-encryption-version + * @problem.severity error + * @precision high + */ + +import csharp + +/** + * Holds if `oc` is creating an object of type `c` = `Azure.Storage.ClientSideEncryptionOptions` + * and `e` is the `version` argument to the contructor + */ +predicate isCreatingAzureClientSideEncryptionObject(ObjectCreation oc, Class c, Expr e) { + exists(Parameter p | p.hasName("version") | + c.getQualifiedName() in ["Azure.Storage.ClientSideEncryptionOptions"] and + oc.getTarget() = c.getAConstructor() and + e = oc.getArgumentForParameter(p) + ) +} + +/** + * Holds if `oc` is an object creation of the outdated type `c` = `Microsoft.Azure.Storage.Blob.BlobEncryptionPolicy` + */ +predicate isCreatingOutdatedAzureClientSideEncryptionObject(ObjectCreation oc, Class c) { + c.getQualifiedName() in ["Microsoft.Azure.Storage.Blob.BlobEncryptionPolicy"] and + oc.getTarget() = c.getAConstructor() +} + +/** + * Holds if the Azure.Storage assembly for `c` is a version knwon to support + * version 2+ for client-side encryption and if the argument for the constructor `version` + * is set to a secure value. + */ +predicate isObjectCreationSafe(Class c, Expr versionExpr, Assembly asm) { + // Check if the Azure.Storage assembly version has the fix + exists(int versionCompare | + versionCompare = asm.getVersion().compareTo("12.12.0.0") and + versionCompare >= 0 + ) and + // and that the version argument for the constructor is guaranteed to be Version2 + isExprAnAccessToSafeClientSideEncryptionVersionValue(versionExpr) +} + +/** + * Holds if the expression `e` is an access to a safe version of the enum `ClientSideEncryptionVersion` + * or an equivalent numeric value + */ +predicate isExprAnAccessToSafeClientSideEncryptionVersionValue(Expr e) { + exists(EnumConstant ec | + ec.hasQualifiedName("Azure.Storage.ClientSideEncryptionVersion.V2_0") and + ec.getAnAccess() = e + ) + or + e.getValue().toInt() >= 2 +} + +from Expr e, Class c, Assembly asm +where + asm = c.getLocation() and + ( + exists(Expr e2 | + isCreatingAzureClientSideEncryptionObject(e, c, e2) and + not isObjectCreationSafe(c, e2, asm) + ) + or + isCreatingOutdatedAzureClientSideEncryptionObject(e, c) + ) +select e, + "Unsafe usage of v1 version of Azure Storage client-side encryption (CVE-2022-PENDING). See http://aka.ms/azstorageclientencryptionblog" diff --git a/java/ql/src/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.java b/java/ql/src/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.java new file mode 100644 index 00000000000..83157c14251 --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.java @@ -0,0 +1,46 @@ + +// BAD: Using an outdated SDK that does not support client side encryption version V2_0 +new EncryptedBlobClientBuilder() + .blobClient(blobClient) + .key(resolver.buildAsyncKeyEncryptionKey(keyid).block(), keyWrapAlgorithm) + .buildEncryptedBlobClient() + .uploadWithResponse(new BlobParallelUploadOptions(data) + .setMetadata(metadata) + .setHeaders(headers) + .setTags(tags) + .setTier(tier) + .setRequestConditions(requestConditions) + .setComputeMd5(computeMd5) + .setParallelTransferOptions(parallelTransferOptions), + timeout, context); + +// BAD: Using the deprecatedd client side encryption version V1_0 +new EncryptedBlobClientBuilder(EncryptionVersion.V1) + .blobClient(blobClient) + .key(resolver.buildAsyncKeyEncryptionKey(keyid).block(), keyWrapAlgorithm) + .buildEncryptedBlobClient() + .uploadWithResponse(new BlobParallelUploadOptions(data) + .setMetadata(metadata) + .setHeaders(headers) + .setTags(tags) + .setTier(tier) + .setRequestConditions(requestConditions) + .setComputeMd5(computeMd5) + .setParallelTransferOptions(parallelTransferOptions), + timeout, context); + + +// GOOD: Using client side encryption version V2_0 +new EncryptedBlobClientBuilder(EncryptionVersion.V2) + .blobClient(blobClient) + .key(resolver.buildAsyncKeyEncryptionKey(keyid).block(), keyWrapAlgorithm) + .buildEncryptedBlobClient() + .uploadWithResponse(new BlobParallelUploadOptions(data) + .setMetadata(metadata) + .setHeaders(headers) + .setTags(tags) + .setTier(tier) + .setRequestConditions(requestConditions) + .setComputeMd5(computeMd5) + .setParallelTransferOptions(parallelTransferOptions), + timeout, context); diff --git a/java/ql/src/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp b/java/ql/src/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp new file mode 100644 index 00000000000..ed05e5f3370 --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp @@ -0,0 +1,31 @@ + + + + + +

    Azure Storage .NET, Java, and Python SDKs support encryption on the client with a customer-managed key that is maintained in Azure Key Vault or another key store.

    +

    Current release versions of the Azure Storage SDKs use cipher block chaining (CBC mode) for client-side encryption (referred to as v1).

    + +
    + + +

    Consider switching to v1 client-side encryption.

    + +
    + + +

    The following example shows an HTTP request parameter being used directly in a forming a +new request without validating the input, which facilitates SSRF attacks. +It also shows how to remedy the problem by validating the user input against a known fixed string. +

    + + + +
    + +
  • + Azure Storage Client Encryption Blog. +
  • + +
    +
    diff --git a/java/ql/src/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql b/java/ql/src/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql new file mode 100644 index 00000000000..7f1ec039d20 --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql @@ -0,0 +1,71 @@ +/** + * @name Unsafe usage of v1 version of Azure Storage client-side encryption (CVE-2022-PENDING). + * @description Unsafe usage of v1 version of Azure Storage client-side encryption, please refer to http://aka.ms/azstorageclientencryptionblog + * @kind problem + * @tags security + * cryptography + * external/cwe/cwe-327 + * @id java/azure-storage/unsafe-client-side-encryption-in-use + * @problem.severity error + * @precision high + */ + +import java + +/** + * Holds if the call `call` is an object creation for a class `EncryptedBlobClientBuilder` + * that takes no arguments, which means that it is using V1 encryption + */ +predicate isCreatingOutdatedAzureClientSideEncryptionObject(Call call, Class c) { + exists(string package, string type, Constructor constructor | + c.hasQualifiedName(package, type) and + c.getAConstructor() = constructor and + call.getCallee() = constructor and + ( + type = "EncryptedBlobClientBuilder" and + package = "com.azure.storage.blob.specialized.cryptography" and + not exists(Expr e | e = call.getArgument(0)) + or + type = "BlobEncryptionPolicy" and package = "com.microsoft.azure.storage.blob" + ) + ) +} + +/** + * Holds if the call `call` is an object creation for a class `EncryptedBlobClientBuilder` + * that takes `versionArg` as the argument for the version. + */ +predicate isCreatingAzureClientSideEncryptionObjectNewVersion(Call call, Class c, Expr versionArg) { + exists(string package, string type, Constructor constructor | + c.hasQualifiedName(package, type) and + c.getAConstructor() = constructor and + call.getCallee() = constructor and + type = "EncryptedBlobClientBuilder" and + package = "com.azure.storage.blob.specialized.cryptography" and + versionArg = call.getArgument(0) + ) +} + +/** + * Holds if the call `call` is an object creation for a class `EncryptedBlobClientBuilder` + * that takes `versionArg` as the argument for the version, and the version number is safe + */ +predicate isCreatingSafeAzureClientSideEncryptionObject(Call call, Class c, Expr versionArg) { + isCreatingAzureClientSideEncryptionObjectNewVersion(call, c, versionArg) and + exists(FieldRead fr, Field f | + fr = versionArg and + f.getAnAccess() = fr and + f.hasQualifiedName("com.azure.storage.blob.specialized.cryptography", "EncryptionVersion", "V2") + ) +} + +from Expr e, Class c +where + exists(Expr argVersion | + isCreatingAzureClientSideEncryptionObjectNewVersion(e, c, argVersion) and + not isCreatingSafeAzureClientSideEncryptionObject(e, c, argVersion) + ) + or + isCreatingOutdatedAzureClientSideEncryptionObject(e, c) +select e, + "Unsafe usage of v1 version of Azure Storage client-side encryption (CVE-2022-PENDING). See http://aka.ms/azstorageclientencryptionblog" diff --git a/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.py b/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.py new file mode 100644 index 00000000000..c4a0519f29d --- /dev/null +++ b/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.py @@ -0,0 +1,7 @@ +blob_client = blob_service_client.get_blob_client(container=container_name, blob=blob_name) + blob_client.require_encryption = True + blob_client.key_encryption_key = kek + # GOOD: Must use `encryption_version` set to `2.0` + blob_client.encryption_version = '2.0' # Use Version 2.0! + with open(“decryptedcontentfile.txtâ€, “rbâ€) as stream: + blob_client.upload_blob(stream, overwrite=OVERWRITE_EXISTING) \ No newline at end of file diff --git a/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp b/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp new file mode 100644 index 00000000000..9571e1150ef --- /dev/null +++ b/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp @@ -0,0 +1,31 @@ + + + + + +

    Azure Storage .NET, Java, and Python SDKs support encryption on the client with a customer-managed key that is maintained in Azure Key Vault or another key store.

    +

    Current release versions of the Azure Storage SDKs use cipher block chaining (CBC mode) for client-side encryption (referred to as v1).

    + +
    + + +

    Consider switching to v1 client-side encryption.

    + +
    + + +

    The following example shows an HTTP request parameter being used directly in a forming a +new request without validating the input, which facilitates SSRF attacks. +It also shows how to remedy the problem by validating the user input against a known fixed string. +

    + + + +
    + +
  • + Azure Storage Client Encryption Blog. +
  • + +
    +
    diff --git a/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql b/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql new file mode 100644 index 00000000000..8db450a5ecb --- /dev/null +++ b/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql @@ -0,0 +1,55 @@ +/** + * @name Unsafe usage of v1 version of Azure Storage client-side encryption (CVE-2022-PENDING). + * @description Unsafe usage of v1 version of Azure Storage client-side encryption, please refer to http://aka.ms/azstorageclientencryptionblog + * @kind problem + * @tags security + * cryptography + * external/cwe/cwe-327 + * @id python/azure-storage/unsafe-client-side-encryption-in-use + * @problem.severity error + * @precision medium + */ + +import python + +predicate isUnsafeClientSideAzureStorageEncryptionViaAttributes(Call call, AttrNode node) { + exists(ControlFlowNode ctrlFlowNode, AssignStmt astmt, Attribute a | + astmt.getATarget() = a and + a.getAttr() in ["key_encryption_key", "key_resolver_function"] and + a.getAFlowNode() = node and + node.strictlyReaches(ctrlFlowNode) and + node != ctrlFlowNode and + call.getAChildNode().(Attribute).getAttr() = "upload_blob" and + ctrlFlowNode = call.getAFlowNode() and + not astmt.getValue() instanceof None and + not exists(AssignStmt astmt2, Attribute a2, AttrNode encryptionVersionSet, StrConst uc | + uc = astmt2.getValue() and + uc.getLiteralValue().toString() in ["'2.0'", "2.0"] and + a2.getAttr() = "encryption_version" and + a2.getAFlowNode() = encryptionVersionSet and + encryptionVersionSet.strictlyReaches(ctrlFlowNode) + ) + ) +} + +predicate isUnsafeClientSideAzureStorageEncryptionViaObjectCreation(Call call, ControlFlowNode node) { + exists(Keyword k | k.getAFlowNode() = node | + call.getFunc().(Name).getId().toString() in [ + "ContainerClient", "BlobClient", "BlobServiceClient" + ] and + k.getArg() = "key_encryption_key" and + k = call.getANamedArg() and + not k.getValue() instanceof None and + not exists(Keyword k2 | k2 = call.getANamedArg() | + k2.getArg() = "encryption_version" and + k2.getValue().(StrConst).getLiteralValue().toString() in ["'2.0'", "2.0"] + ) + ) +} + +from Call call, ControlFlowNode node +where + isUnsafeClientSideAzureStorageEncryptionViaAttributes(call, node) or + isUnsafeClientSideAzureStorageEncryptionViaObjectCreation(call, node) +select node, + "Unsafe usage of v1 version of Azure Storage client-side encryption (CVE-2022-PENDING). See http://aka.ms/azstorageclientencryptionblog" From f53adca1083de22e0a347f893376a34d3aa66d5a Mon Sep 17 00:00:00 2001 From: ihsinme Date: Mon, 4 Jul 2022 11:10:02 +0300 Subject: [PATCH 137/505] Update DangerousUseMbtowc.ql --- .../CWE/CWE-125/DangerousUseMbtowc.ql | 206 ++++++++++++++---- 1 file changed, 167 insertions(+), 39 deletions(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.ql b/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.ql index 2b668025794..da995ef48cb 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.ql @@ -23,7 +23,7 @@ predicate exprMayBeString(Expr exp) { fctmp.getAnArgument().(VariableAccess).getTarget() = exp.(VariableAccess).getTarget() or globalValueNumber(fctmp.getAnArgument()) = globalValueNumber(exp) ) and - fctmp.getTarget().hasGlobalOrStdName(["strlen", "strcat", "strncat", "strcpy", "sptintf"]) + fctmp.getTarget().hasName(["strlen", "strcat", "strncat", "strcpy", "sptintf", "printf"]) ) or exists(AssignExpr astmp | @@ -62,48 +62,176 @@ predicate argMacro(Expr exp) { ) } -from FunctionCall fc, string msg -where - exists(Loop lptmp | lptmp = fc.getEnclosingStmt().getParentStmt*()) and - fc.getTarget().hasGlobalOrStdName(["mbtowc", "mbrtowc"]) and - not fc.getArgument(0).isConstant() and - not fc.getArgument(1).isConstant() and - ( - exprMayBeString(fc.getArgument(1)) and - argConstOrSizeof(fc.getArgument(2)) and - fc.getArgument(2).getValue().toInt() < 5 and - not argMacro(fc.getArgument(2)) and - msg = "Size can be less than maximum character length, use macro MB_CUR_MAX." - or - not exprMayBeString(fc.getArgument(1)) and +/** Holds if erroneous situations of using functions `mbtowc` and `mbrtowc` are detected. */ +predicate findUseCharacterConversion(Expr exp, string msg) { + exists(FunctionCall fc | + fc = exp and ( - argConstOrSizeof(fc.getArgument(2)) - or - argMacro(fc.getArgument(2)) - or - exists(DecrementOperation dotmp | - globalValueNumber(dotmp.getAnOperand()) = globalValueNumber(fc.getArgument(2)) and - not exists(AssignSubExpr aetmp | + exists(Loop lptmp | lptmp = fc.getEnclosingStmt().getParentStmt*()) and + fc.getTarget().hasName(["mbtowc", "mbrtowc"]) and + not fc.getArgument(0).isConstant() and + not fc.getArgument(1).isConstant() and + ( + exprMayBeString(fc.getArgument(1)) and + argConstOrSizeof(fc.getArgument(2)) and + fc.getArgument(2).getValue().toInt() < 5 and + not argMacro(fc.getArgument(2)) and + msg = "Size can be less than maximum character length, use macro MB_CUR_MAX." + or + not exprMayBeString(fc.getArgument(1)) and + ( + argConstOrSizeof(fc.getArgument(2)) + or + argMacro(fc.getArgument(2)) + or + exists(DecrementOperation dotmp | + globalValueNumber(dotmp.getAnOperand()) = globalValueNumber(fc.getArgument(2)) and + not exists(AssignSubExpr aetmp | + ( + aetmp.getLValue().(VariableAccess).getTarget() = + fc.getArgument(2).(VariableAccess).getTarget() or + globalValueNumber(aetmp.getLValue()) = globalValueNumber(fc.getArgument(2)) + ) and + globalValueNumber(aetmp.getRValue()) = globalValueNumber(fc) + ) + ) + ) and + msg = + "Access beyond the allocated memory is possible, the length can change without changing the pointer." + or + exists(AssignPointerAddExpr aetmp | ( aetmp.getLValue().(VariableAccess).getTarget() = - fc.getArgument(2).(VariableAccess).getTarget() or - globalValueNumber(aetmp.getLValue()) = globalValueNumber(fc.getArgument(2)) + fc.getArgument(0).(VariableAccess).getTarget() or + globalValueNumber(aetmp.getLValue()) = globalValueNumber(fc.getArgument(0)) ) and globalValueNumber(aetmp.getRValue()) = globalValueNumber(fc) - ) + ) and + msg = "Maybe you're using the function's return value incorrectly." ) - ) and - msg = - "Access beyond the allocated memory is possible, the length can change without changing the pointer." - or - exists(AssignPointerAddExpr aetmp | - ( - aetmp.getLValue().(VariableAccess).getTarget() = - fc.getArgument(0).(VariableAccess).getTarget() or - globalValueNumber(aetmp.getLValue()) = globalValueNumber(fc.getArgument(0)) - ) and - globalValueNumber(aetmp.getRValue()) = globalValueNumber(fc) - ) and - msg = "Maybe you're using the function's return value incorrectly." + ) ) -select fc, msg +} + +/** Holds if detecting erroneous situations of working with multibyte characters. */ +predicate findUseMultibyteCharacter(Expr exp, string msg) { + exists(ArrayType arrayType, ArrayExpr arrayExpr | + arrayExpr = exp and + arrayExpr.getArrayBase().(VariableAccess).getTarget().getADeclarationEntry().getType() = + arrayType and + ( + exists(AssignExpr assZero, SizeofExprOperator sizeofArray, Expr oneValue | + oneValue.getValue() = "1" and + sizeofArray.getExprOperand().getType() = arrayType and + assZero.getLValue() = arrayExpr and + arrayExpr.getArrayOffset().(SubExpr).hasOperands(sizeofArray, oneValue) and + assZero.getRValue().getValue() = "0" + ) and + arrayType.getArraySize() != arrayType.getByteSize() and + msg = + "The size of the array element is greater than one byte, so the offset will point outside the array." + or + exists(FunctionCall mbFunction | + ( + mbFunction.getTarget().getName().matches("_mbs%") or + mbFunction.getTarget().getName().matches("mbs%") or + mbFunction.getTarget().getName().matches("_mbc%") or + mbFunction.getTarget().getName().matches("mbc%") + ) and + mbFunction.getAnArgument().(VariableAccess).getTarget().getADeclarationEntry().getType() = + arrayType + ) and + exists(Loop loop, SizeofExprOperator sizeofArray, AssignExpr assignExpr | + arrayExpr.getEnclosingStmt().getParentStmt*() = loop and + sizeofArray.getExprOperand().getType() = arrayType and + assignExpr.getLValue() = arrayExpr and + loop.getCondition().(LTExpr).getLeftOperand().(VariableAccess).getTarget() = + arrayExpr.getArrayOffset().getAChild*().(VariableAccess).getTarget() and + loop.getCondition().(LTExpr).getRightOperand() = sizeofArray + ) and + msg = + "This buffer may contain multibyte characters, so attempting to copy may result in part of the last character being lost." + ) + ) + or + exists(FunctionCall mbccpy, Loop loop, SizeofExprOperator sizeofOp | + mbccpy.getTarget().hasName("_mbccpy") and + mbccpy.getArgument(0) = exp and + exp.getEnclosingStmt().getParentStmt*() = loop and + sizeofOp.getExprOperand().getType() = + exp.getAChild*().(VariableAccess).getTarget().getADeclarationEntry().getType() and + loop.getCondition().(LTExpr).getLeftOperand().(VariableAccess).getTarget() = + exp.getAChild*().(VariableAccess).getTarget() and + loop.getCondition().(LTExpr).getRightOperand() = sizeofOp and + msg = + "This buffer may contain multibyte characters, so an attempt to copy may result in an overflow." + ) +} + +/** Holds if erroneous situations of using functions `MultiByteToWideChar` and `WideCharToMultiByte` or `mbstowcs` and `_mbstowcs_l` and `mbsrtowcs` are detected. */ +predicate findUseStringConversion( + Expr exp, string msg, int posBufSrc, int posBufDst, int posSizeDst, string nameCalls +) { + exists(FunctionCall fc | + fc = exp and + posBufSrc in [0 .. fc.getNumberOfArguments() - 1] and + posSizeDst in [0 .. fc.getNumberOfArguments() - 1] and + ( + fc.getTarget().hasName(nameCalls) and + ( + globalValueNumber(fc.getArgument(posBufDst)) = globalValueNumber(fc.getArgument(posBufSrc)) and + msg = + "According to the definition of the functions, if the source buffer and the destination buffer are the same, undefined behavior is possible." + or + exists(ArrayType arrayDst | + fc.getArgument(posBufDst).(VariableAccess).getTarget().getADeclarationEntry().getType() = + arrayDst and + fc.getArgument(posSizeDst).getValue().toInt() >= arrayDst.getArraySize() and + not exists(AssignExpr assZero | + assZero.getLValue().(ArrayExpr).getArrayBase().(VariableAccess).getTarget() = + fc.getArgument(posBufDst).(VariableAccess).getTarget() and + assZero.getRValue().getValue() = "0" + ) and + not exists(Expr someExp, FunctionCall checkSize | + checkSize.getASuccessor*() = fc and + checkSize.getTarget().hasName(nameCalls) and + checkSize.getArgument(posSizeDst).getValue() = "0" and + globalValueNumber(checkSize) = globalValueNumber(someExp) and + someExp.getEnclosingStmt().getParentStmt*() instanceof IfStmt + ) and + exprMayBeString(fc.getArgument(posBufDst)) and + msg = + "According to the definition of the functions, it is not guaranteed to write a null character at the end of the string, so access beyond the bounds of the destination buffer is possible." + ) + or + exists(FunctionCall allocMem | + allocMem.getTarget().hasName(["calloc", "malloc"]) and + globalValueNumber(fc.getArgument(posBufDst)) = globalValueNumber(allocMem) and + ( + allocMem.getArgument(allocMem.getNumberOfArguments() - 1).getValue() = "1" or + not exists(SizeofOperator sizeofOperator | + globalValueNumber(allocMem + .getArgument(allocMem.getNumberOfArguments() - 1) + .getAChild*()) = globalValueNumber(sizeofOperator) + ) + ) and + msg = + "The buffer destination has a type other than char, you need to take this into account when allocating memory." + ) + or + fc.getArgument(posBufDst).getValue() = "0" and + fc.getArgument(posSizeDst).getValue() != "0" and + msg = + "If the destination buffer is NULL and its size is not 0, then undefined behavior is possible." + ) + ) + ) +} + +from Expr exp, string msg +where + findUseCharacterConversion(exp, msg) or + findUseMultibyteCharacter(exp, msg) or + findUseStringConversion(exp, msg, 1, 0, 2, ["mbstowcs", "_mbstowcs_l", "mbsrtowcs"]) or + findUseStringConversion(exp, msg, 2, 4, 5, ["MultiByteToWideChar", "WideCharToMultiByte"]) +select exp, msg + exp.getEnclosingFunction().getName() From 6d800de377290d4566683214a5d7ed1f1795753b Mon Sep 17 00:00:00 2001 From: ihsinme Date: Mon, 4 Jul 2022 11:11:49 +0300 Subject: [PATCH 138/505] Create test1.cpp --- .../CWE/CWE-125/semmle/tests/test1.cpp | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test1.cpp diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test1.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test1.cpp new file mode 100644 index 00000000000..828b91a44f1 --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test1.cpp @@ -0,0 +1,95 @@ +#define CP_ACP 1 +#define CP_UTF8 1 +#define WC_COMPOSITECHECK 1 +#define NULL 0 +typedef unsigned int UINT; +typedef unsigned long DWORD, *PDWORD, *LPDWORD; +typedef char CHAR; +#define CONST const +typedef wchar_t WCHAR; + +typedef CHAR *LPSTR; +typedef CONST CHAR *LPCSTR; +typedef CONST WCHAR *LPCWSTR; + +typedef int BOOL; +typedef BOOL *LPBOOL; + + +int WideCharToMultiByte(UINT CodePage,DWORD dwFlags,LPCWSTR lpWideCharStr,int cchWideChar,LPSTR lpMultiByteStr,int cbMultiByte,LPCWSTR lpDefaultChar,LPBOOL lpUsedDefaultChar); +int MultiByteToWideChar(UINT CodePage,DWORD dwFlags,LPCSTR lpMultiByteStr,int cbMultiByte,LPCWSTR lpWideCharStr,int cchWideChar); + +int printf ( const char * format, ... ); +typedef unsigned int size_t; +void* calloc (size_t num, size_t size); +void* malloc (size_t size); + +void badTest1(void *src, int size) { + WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)src, -1, (LPSTR)src, size, 0, 0); // BAD + MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, (LPCWSTR)src, 30); // BAD +} +void goodTest2(){ + wchar_t src[] = L"0123456789ABCDEF"; + char dst[16]; + int res = WideCharToMultiByte(CP_UTF8, 0, src, -1, dst, 16, NULL, NULL); // GOOD + if (res == sizeof(dst)) { + dst[res-1] = NULL; + } else { + dst[res] = NULL; + } + printf("%s\n", dst); +} +void badTest2(){ + wchar_t src[] = L"0123456789ABCDEF"; + char dst[16]; + WideCharToMultiByte(CP_UTF8, 0, src, -1, dst, 16, NULL, NULL); // BAD + printf("%s\n", dst); +} +void goodTest3(){ + char src[] = "0123456789ABCDEF"; + int size = MultiByteToWideChar(CP_UTF8, 0, src,sizeof(src),NULL,0); + wchar_t * dst = (wchar_t*)calloc(size + 1, sizeof(wchar_t)); + MultiByteToWideChar(CP_UTF8, 0, src, -1, dst, size+1); // GOOD +} +void badTest3(){ + char src[] = "0123456789ABCDEF"; + int size = MultiByteToWideChar(CP_UTF8, 0, src,sizeof(src),NULL,0); + wchar_t * dst = (wchar_t*)calloc(size + 1, 1); + MultiByteToWideChar(CP_UTF8, 0, src, -1, dst, size+1); // BAD +} +void goodTest4(){ + char src[] = "0123456789ABCDEF"; + int size = MultiByteToWideChar(CP_UTF8, 0, src,sizeof(src),NULL,0); + wchar_t * dst = (wchar_t*)malloc((size + 1)*sizeof(wchar_t)); + MultiByteToWideChar(CP_UTF8, 0, src, -1, dst, size+1); // GOOD +} +void badTest4(){ + char src[] = "0123456789ABCDEF"; + int size = MultiByteToWideChar(CP_UTF8, 0, src,sizeof(src),NULL,0); + wchar_t * dst = (wchar_t*)malloc(size + 1); + MultiByteToWideChar(CP_UTF8, 0, src, -1, dst, size+1); // BAD +} +int goodTest5(void *src){ + return WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)src, -1, 0, 0, 0, 0); // GOOD +} +int badTest5 (void *src) { + return WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)src, -1, 0, 3, 0, 0); // BAD +} +void goodTest6(WCHAR *src) +{ + int size; + char dst[5] =""; + size = WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, src, -1, dst, 0, 0, 0); + if(size>=sizeof(dst)){ + printf("buffer size error\n"); + return; + } + WideCharToMultiByte(CP_ACP, 0, src, -1, dst, sizeof(dst), 0, 0); // GOOD + printf("%s\n", dst); +} +void badTest6(WCHAR *src) +{ + char dst[5] =""; + WideCharToMultiByte(CP_ACP, 0, src, -1, dst, 260, 0, 0); // BAD + printf("%s\n", dst); +} From 1ce42dcd306a77a7fbb90c7a0a3f9e16acc45523 Mon Sep 17 00:00:00 2001 From: ihsinme Date: Mon, 4 Jul 2022 11:12:34 +0300 Subject: [PATCH 139/505] Create test2.cpp --- .../CWE/CWE-125/semmle/tests/test2.cpp | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test2.cpp diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test2.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test2.cpp new file mode 100644 index 00000000000..99dc3e47e5b --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test2.cpp @@ -0,0 +1,82 @@ +#define NULL 0 +typedef unsigned int size_t; +struct mbstate_t{}; +struct _locale_t{}; +int printf ( const char * format, ... ); +void* calloc (size_t num, size_t size); +void* malloc (size_t size); + +size_t mbstowcs(wchar_t *wcstr,const char *mbstr,size_t count); +size_t _mbstowcs_l(wchar_t *wcstr,const char *mbstr,size_t count, _locale_t locale); +size_t mbsrtowcs(wchar_t *wcstr,const char *mbstr,size_t count, mbstate_t *mbstate); + + +void badTest1(void *src, int size) { + mbstowcs((wchar_t*)src,(char*)src,size); // BAD + _locale_t locale; + _mbstowcs_l((wchar_t*)src,(char*)src,size,locale); // BAD + mbstate_t *mbstate; + mbsrtowcs((wchar_t*)src,(char*)src,size,mbstate); // BAD +} +void goodTest2(){ + char src[] = "0123456789ABCDEF"; + wchar_t dst[16]; + int res = mbstowcs(dst, src,16); // GOOD + if (res == sizeof(dst)) { + dst[res-1] = NULL; + } else { + dst[res] = NULL; + } + printf("%s\n", dst); +} +void badTest2(){ + char src[] = "0123456789ABCDEF"; + wchar_t dst[16]; + mbstowcs(dst, src,16); // BAD + printf("%s\n", dst); +} +void goodTest3(){ + char src[] = "0123456789ABCDEF"; + int size = mbstowcs(NULL, src,NULL); + wchar_t * dst = (wchar_t*)calloc(size + 1, sizeof(wchar_t)); + mbstowcs(dst, src,size+1); // GOOD +} +void badTest3(){ + char src[] = "0123456789ABCDEF"; + int size = mbstowcs(NULL, src,NULL); + wchar_t * dst = (wchar_t*)calloc(size + 1, 1); + mbstowcs(dst, src,size+1); // BAD +} +void goodTest4(){ + char src[] = "0123456789ABCDEF"; + int size = mbstowcs(NULL, src,NULL); + wchar_t * dst = (wchar_t*)malloc((size + 1)*sizeof(wchar_t)); + mbstowcs(dst, src,size+1); // GOOD +} +void badTest4(){ + char src[] = "0123456789ABCDEF"; + int size = mbstowcs(NULL, src,NULL); + wchar_t * dst = (wchar_t*)malloc(size + 1); + mbstowcs(dst, src,size+1); // BAD +} +int goodTest5(void *src){ + return mbstowcs(NULL, (char*)src,NULL); // GOOD +} +int badTest5 (void *src) { + return mbstowcs(NULL, (char*)src,3); // BAD +} +void goodTest6(void *src){ + wchar_t dst[5]; + int size = mbstowcs(NULL, (char*)src,NULL); + if(size>=sizeof(dst)){ + printf("buffer size error\n"); + return; + } + mbstowcs(dst, (char*)src,sizeof(dst)); // GOOD + printf("%s\n", dst); +} +void badTest6(void *src){ + wchar_t dst[5]; + mbstowcs(dst, (char*)src,260); // BAD + printf("%s\n", dst); +} From 4e28887689f41d4c18362d864f38c8ac243508bb Mon Sep 17 00:00:00 2001 From: ihsinme Date: Mon, 4 Jul 2022 11:13:07 +0300 Subject: [PATCH 140/505] Create test3.cpp --- .../CWE/CWE-125/semmle/tests/test3.cpp | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test3.cpp diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test3.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test3.cpp new file mode 100644 index 00000000000..e37052e839b --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test3.cpp @@ -0,0 +1,48 @@ +#define NULL 0 +typedef unsigned int size_t; + +unsigned char * _mbsnbcpy(unsigned char * strDest,const unsigned char * strSource,size_t count); +size_t _mbclen(const unsigned char *c); +void _mbccpy(unsigned char *dest,const unsigned char *src); +unsigned char *_mbsinc(const unsigned char *current); +void goodTest1(unsigned char *src){ + unsigned char dst[50]; + _mbsnbcpy(dst,src,sizeof(dst)); // GOOD +} +size_t badTest1(unsigned char *src){ + int cb = 0; + unsigned char dst[50]; + while( cb < sizeof(dst) ) + dst[cb++]=*src++; // BAD + return _mbclen(dst); +} +void goodTest2(unsigned char *src){ + + int cb = 0; + unsigned char dst[50]; + while( (cb + _mbclen(src)) <= sizeof(dst) ) + { + _mbccpy(dst+cb,src); // GOOD + cb+=_mbclen(src); + src=_mbsinc(src); + } +} +void badTest2(unsigned char *src){ + + int cb = 0; + unsigned char dst[50]; + while( cb < sizeof(dst) ) + { + _mbccpy(dst+cb,src); // BAD + cb+=_mbclen(src); + src=_mbsinc(src); + } +} +void goodTest3(){ + wchar_t name[50]; + name[sizeof(name) / sizeof(*name) - 1] = L'\0'; // GOOD +} +void badTest3(){ + wchar_t name[50]; + name[sizeof(name) - 1] = L'\0'; // BAD +} From 8967f57bbc0652f4cca0131dbe4b845fff7d1056 Mon Sep 17 00:00:00 2001 From: ihsinme Date: Mon, 4 Jul 2022 11:17:12 +0300 Subject: [PATCH 141/505] Update DangerousUseMbtowc.ql --- .../src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.ql b/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.ql index da995ef48cb..7b17e30e24e 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.ql @@ -68,7 +68,7 @@ predicate findUseCharacterConversion(Expr exp, string msg) { fc = exp and ( exists(Loop lptmp | lptmp = fc.getEnclosingStmt().getParentStmt*()) and - fc.getTarget().hasName(["mbtowc", "mbrtowc"]) and + fc.getTarget().hasName(["mbtowc", "mbrtowc", "_mbtowc_l"]) and not fc.getArgument(0).isConstant() and not fc.getArgument(1).isConstant() and ( From 56060e06107d60feee0c83b59e8de159e76632bc Mon Sep 17 00:00:00 2001 From: Raul Garcia <42392023+raulgarciamsft@users.noreply.github.com> Date: Tue, 5 Jul 2022 13:57:28 -0700 Subject: [PATCH 142/505] Update csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp Co-authored-by: intrigus-lgtm <60750685+intrigus-lgtm@users.noreply.github.com> --- .../Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp b/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp index 3bf46ab9f89..49aa5623570 100644 --- a/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp +++ b/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp @@ -9,7 +9,7 @@ -

    Consider switching to v1 client-side encryption.

    +

    Consider switching to v2 client-side encryption.

    From f5c6b45014aedac11b614b65fe7bcb59dfe519c9 Mon Sep 17 00:00:00 2001 From: Raul Garcia <42392023+raulgarciamsft@users.noreply.github.com> Date: Tue, 5 Jul 2022 13:58:11 -0700 Subject: [PATCH 143/505] Update UnsafeUsageOfClientSideEncryptionVersion.qhelp --- .../Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/src/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp b/java/ql/src/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp index ed05e5f3370..3fca30a7926 100644 --- a/java/ql/src/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp +++ b/java/ql/src/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp @@ -9,7 +9,7 @@ -

    Consider switching to v1 client-side encryption.

    +

    Consider switching to v2 client-side encryption.

    From dd1a9a22e3027836f2be9524ebac4cd8fe2f320a Mon Sep 17 00:00:00 2001 From: Raul Garcia <42392023+raulgarciamsft@users.noreply.github.com> Date: Tue, 5 Jul 2022 13:58:38 -0700 Subject: [PATCH 144/505] Update UnsafeUsageOfClientSideEncryptionVersion.qhelp --- .../Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp b/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp index 9571e1150ef..c4111a166de 100644 --- a/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp +++ b/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp @@ -9,7 +9,7 @@ -

    Consider switching to v1 client-side encryption.

    +

    Consider switching to v2 client-side encryption.

    From 74ff579dbc0bbea1d9353382a17ebac3337e208a Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Wed, 6 Jul 2022 15:19:36 -0400 Subject: [PATCH 145/505] Fixing logic bug with LogicalAndExpr --- cpp/ql/lib/semmle/code/cpp/controlflow/Nullness.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/controlflow/Nullness.qll b/cpp/ql/lib/semmle/code/cpp/controlflow/Nullness.qll index 40c975873b4..fac6a57f8cf 100644 --- a/cpp/ql/lib/semmle/code/cpp/controlflow/Nullness.qll +++ b/cpp/ql/lib/semmle/code/cpp/controlflow/Nullness.qll @@ -46,7 +46,7 @@ predicate nullCheckExpr(Expr checkExpr, Variable var) { or exists(LogicalAndExpr op, AnalysedExpr child | expr = op and - op.getRightOperand() = child and + (op.getRightOperand() = child or op.getLeftOperand() = child) and nullCheckExpr(child, v) ) or @@ -99,7 +99,7 @@ predicate validCheckExpr(Expr checkExpr, Variable var) { or exists(LogicalAndExpr op, AnalysedExpr child | expr = op and - op.getRightOperand() = child and + (op.getRightOperand() = child or op.getLeftOperand() = child) and validCheckExpr(child, v) ) or From 7d6fb7f91a2deaab8855a8b2060d866d97029631 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Wed, 6 Jul 2022 21:52:13 +0200 Subject: [PATCH 146/505] C++: Rename LossyFunctionResultCast tests to be correctly named --- ...castFromBitfield.expected => LossyFunctionResultCast.expected} | 0 ...itDowncastFromBitfield.qlref => LossyFunctionResultCast.qlref} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename cpp/ql/test/query-tests/Likely Bugs/Conversion/LossyFunctionResultCast/{ImplicitDowncastFromBitfield.expected => LossyFunctionResultCast.expected} (100%) rename cpp/ql/test/query-tests/Likely Bugs/Conversion/LossyFunctionResultCast/{ImplicitDowncastFromBitfield.qlref => LossyFunctionResultCast.qlref} (100%) diff --git a/cpp/ql/test/query-tests/Likely Bugs/Conversion/LossyFunctionResultCast/ImplicitDowncastFromBitfield.expected b/cpp/ql/test/query-tests/Likely Bugs/Conversion/LossyFunctionResultCast/LossyFunctionResultCast.expected similarity index 100% rename from cpp/ql/test/query-tests/Likely Bugs/Conversion/LossyFunctionResultCast/ImplicitDowncastFromBitfield.expected rename to cpp/ql/test/query-tests/Likely Bugs/Conversion/LossyFunctionResultCast/LossyFunctionResultCast.expected diff --git a/cpp/ql/test/query-tests/Likely Bugs/Conversion/LossyFunctionResultCast/ImplicitDowncastFromBitfield.qlref b/cpp/ql/test/query-tests/Likely Bugs/Conversion/LossyFunctionResultCast/LossyFunctionResultCast.qlref similarity index 100% rename from cpp/ql/test/query-tests/Likely Bugs/Conversion/LossyFunctionResultCast/ImplicitDowncastFromBitfield.qlref rename to cpp/ql/test/query-tests/Likely Bugs/Conversion/LossyFunctionResultCast/LossyFunctionResultCast.qlref From 0b471c2007e66938250bea985afeb8f6851660e8 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Wed, 6 Jul 2022 21:53:12 +0200 Subject: [PATCH 147/505] C++: Improve LossyFunctionResultCast join order Before on wireshark: ``` Tuple counts for #select#ff@eca61bf2: 180100 ~2% {2} r1 = SCAN Type::Type::getUnderlyingType#dispred#f0820431#ff OUTPUT In.1, In.0 84 ~2% {2} r2 = JOIN r1 WITH project#Type::FloatingPointType#class#2e8eb3ef#fffff ON FIRST 1 OUTPUT Lhs.1, Rhs.0 2021 ~0% {2} r3 = JOIN r2 WITH Function::Function::getType#dispred#f0820431#fb_10#join_rhs ON FIRST 1 OUTPUT Rhs.1, Lhs.1 2437 ~0% {2} r4 = JOIN r3 WITH Call::FunctionCall::getTarget#dispred#f0820431#ff_10#join_rhs ON FIRST 1 OUTPUT Lhs.1, Rhs.1 2150 ~0% {2} r5 = r4 AND NOT LossyFunctionResultCast::whiteListWrapped#377b528a#f(Lhs.1) 2150 ~0% {2} r6 = SCAN r5 OUTPUT In.1, In.0 313 ~0% {3} r7 = JOIN r6 WITH exprconv ON FIRST 1 OUTPUT Rhs.1, Lhs.1, Lhs.0 313 ~0% {3} r8 = JOIN r7 WITH Cast::Conversion#class#1f33e835#b ON FIRST 1 OUTPUT Lhs.0, Lhs.1, Lhs.2 148 ~3% {2} r9 = JOIN r8 WITH Expr::Expr::isCompilerGenerated#f0820431#b ON FIRST 1 OUTPUT Lhs.2, Lhs.1 148 ~1% {3} r10 = JOIN r9 WITH Expr::Expr::getActualType#dispred#f0820431#bf ON FIRST 1 OUTPUT Rhs.1, Lhs.1, Lhs.0 21 ~0% {3} r11 = JOIN r10 WITH Type::IntegralType#class#2e8eb3ef#ff ON FIRST 1 OUTPUT Lhs.1, Lhs.2, Lhs.0 21 ~0% {3} r12 = JOIN r11 WITH Element::ElementBase::toString#dispred#f0820431#ff ON FIRST 1 OUTPUT Lhs.2, Lhs.1, Rhs.1 21 ~0% {2} r13 = JOIN r12 WITH Element::ElementBase::toString#dispred#f0820431#ff ON FIRST 1 OUTPUT Lhs.1, ("Return value of type " ++ Lhs.2 ++ " is implicitly converted to " ++ Rhs.1 ++ " here.") return r13 ``` After: ``` Tuple counts for #select#ff@a5a185eg: 20 ~0% {2} r1 = SCAN project#Type::FloatingPointType#class#2e8eb3ef#fffff OUTPUT In.0, In.0 20 ~0% {2} r2 = JOIN r1 WITH project#Type::FloatingPointType#class#2e8eb3ef#fffff ON FIRST 1 OUTPUT Lhs.1, Lhs.0 84 ~2% {2} r3 = JOIN r2 WITH Type::Type::getUnderlyingType#dispred#f0820431#ff_10#join_rhs ON FIRST 1 OUTPUT Rhs.1, Lhs.1 2021 ~0% {2} r4 = JOIN r3 WITH Function::Function::getType#dispred#f0820431#fb_10#join_rhs ON FIRST 1 OUTPUT Rhs.1, Lhs.1 2437 ~0% {2} r5 = JOIN r4 WITH Call::FunctionCall::getTarget#dispred#f0820431#ff_10#join_rhs ON FIRST 1 OUTPUT Lhs.1, Rhs.1 2150 ~0% {2} r6 = r5 AND NOT LossyFunctionResultCast::whiteListWrapped#377b528a#f(Lhs.1) 2150 ~0% {2} r7 = SCAN r6 OUTPUT In.1, In.0 313 ~0% {3} r8 = JOIN r7 WITH exprconv ON FIRST 1 OUTPUT Rhs.1, Lhs.1, Lhs.0 313 ~0% {3} r9 = JOIN r8 WITH Cast::Conversion#class#1f33e835#b ON FIRST 1 OUTPUT Lhs.0, Lhs.1, Lhs.2 148 ~3% {2} r10 = JOIN r9 WITH Expr::Expr::isCompilerGenerated#f0820431#b ON FIRST 1 OUTPUT Lhs.2, Lhs.1 148 ~1% {3} r11 = JOIN r10 WITH Expr::Expr::getActualType#dispred#f0820431#bf ON FIRST 1 OUTPUT Rhs.1, Lhs.1, Lhs.0 21 ~0% {3} r12 = JOIN r11 WITH Type::IntegralType#class#2e8eb3ef#ff ON FIRST 1 OUTPUT Lhs.1, Lhs.2, Lhs.0 21 ~0% {3} r13 = JOIN r12 WITH Element::ElementBase::toString#dispred#f0820431#ff ON FIRST 1 OUTPUT Lhs.2, Lhs.1, Rhs.1 21 ~0% {2} r14 = JOIN r13 WITH Element::ElementBase::toString#dispred#f0820431#ff ON FIRST 1 OUTPUT Lhs.1, ("Return value of type " ++ Lhs.2 ++ " is implicitly converted to " ++ Rhs.1 ++ " here.") return r14 ``` --- cpp/ql/src/Likely Bugs/Conversion/LossyFunctionResultCast.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/Likely Bugs/Conversion/LossyFunctionResultCast.ql b/cpp/ql/src/Likely Bugs/Conversion/LossyFunctionResultCast.ql index dee723e2686..3cbcffe0ce3 100644 --- a/cpp/ql/src/Likely Bugs/Conversion/LossyFunctionResultCast.ql +++ b/cpp/ql/src/Likely Bugs/Conversion/LossyFunctionResultCast.ql @@ -44,7 +44,7 @@ predicate whiteListWrapped(FunctionCall fc) { from FunctionCall c, FloatingPointType t1, IntegralType t2 where - t1 = c.getTarget().getType().getUnderlyingType() and + pragma[only_bind_into](t1) = c.getTarget().getType().getUnderlyingType() and t2 = c.getActualType() and c.hasImplicitConversion() and not whiteListWrapped(c) From 01da877d0ebb101ba7ccf1c33c6cb113fb0d930b Mon Sep 17 00:00:00 2001 From: Raul Garcia Date: Wed, 6 Jul 2022 14:07:14 -0700 Subject: [PATCH 148/505] Moving the new query to experimental. It was added to the wrong folder initially. --- .../CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.java | 0 .../CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp | 0 .../CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename java/ql/src/{ => experimental}/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.java (100%) rename java/ql/src/{ => experimental}/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp (100%) rename java/ql/src/{ => experimental}/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql (100%) diff --git a/java/ql/src/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.java b/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.java similarity index 100% rename from java/ql/src/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.java rename to java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.java diff --git a/java/ql/src/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp b/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp similarity index 100% rename from java/ql/src/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp rename to java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp diff --git a/java/ql/src/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql b/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql similarity index 100% rename from java/ql/src/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql rename to java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql From 4379aa4398f59d48cba6efdbcc75cc0afc1db68d Mon Sep 17 00:00:00 2001 From: "REDMOND\\brodes" Date: Thu, 7 Jul 2022 10:32:36 -0400 Subject: [PATCH 149/505] Adding Initializer in condition as an occurance of isDef --- cpp/ql/lib/semmle/code/cpp/controlflow/Nullness.qll | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cpp/ql/lib/semmle/code/cpp/controlflow/Nullness.qll b/cpp/ql/lib/semmle/code/cpp/controlflow/Nullness.qll index fac6a57f8cf..92beb5c9d99 100644 --- a/cpp/ql/lib/semmle/code/cpp/controlflow/Nullness.qll +++ b/cpp/ql/lib/semmle/code/cpp/controlflow/Nullness.qll @@ -169,7 +169,10 @@ class AnalysedExpr extends Expr { */ predicate isDef(LocalScopeVariable v) { this.inCondition() and - this.(Assignment).getLValue() = v.getAnAccess() + ( + this.(Assignment).getLValue() = v.getAnAccess() or + exists(Initializer i | this.getEnclosingStmt() = i.getEnclosingStmt() and v = i.getDeclaration()) + ) } /** From f8994d04d652af02b888221feacb37763016aece Mon Sep 17 00:00:00 2001 From: Raul Garcia Date: Thu, 7 Jul 2022 11:49:05 -0700 Subject: [PATCH 150/505] Clean up --- .../Azure/UnsafeUsageOfClientSideEncryptionVersion.ql | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql b/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql index cb986754958..0de3d020e4f 100644 --- a/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql +++ b/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql @@ -18,7 +18,7 @@ import csharp */ predicate isCreatingAzureClientSideEncryptionObject(ObjectCreation oc, Class c, Expr e) { exists(Parameter p | p.hasName("version") | - c.getQualifiedName() in ["Azure.Storage.ClientSideEncryptionOptions"] and + c.hasQualifiedName("Azure.Storage.ClientSideEncryptionOptions") and oc.getTarget() = c.getAConstructor() and e = oc.getArgumentForParameter(p) ) @@ -28,7 +28,7 @@ predicate isCreatingAzureClientSideEncryptionObject(ObjectCreation oc, Class c, * Holds if `oc` is an object creation of the outdated type `c` = `Microsoft.Azure.Storage.Blob.BlobEncryptionPolicy` */ predicate isCreatingOutdatedAzureClientSideEncryptionObject(ObjectCreation oc, Class c) { - c.getQualifiedName() in ["Microsoft.Azure.Storage.Blob.BlobEncryptionPolicy"] and + c.hasQualifiedName("Microsoft.Azure.Storage.Blob.BlobEncryptionPolicy") and oc.getTarget() = c.getAConstructor() } @@ -37,7 +37,7 @@ predicate isCreatingOutdatedAzureClientSideEncryptionObject(ObjectCreation oc, C * version 2+ for client-side encryption and if the argument for the constructor `version` * is set to a secure value. */ -predicate isObjectCreationSafe(Class c, Expr versionExpr, Assembly asm) { +predicate isObjectCreationSafe(Expr versionExpr, Assembly asm) { // Check if the Azure.Storage assembly version has the fix exists(int versionCompare | versionCompare = asm.getVersion().compareTo("12.12.0.0") and @@ -66,7 +66,7 @@ where ( exists(Expr e2 | isCreatingAzureClientSideEncryptionObject(e, c, e2) and - not isObjectCreationSafe(c, e2, asm) + not isObjectCreationSafe(e2, asm) ) or isCreatingOutdatedAzureClientSideEncryptionObject(e, c) From 2f1cfa816fd7eefb3bab86dae9ed1344479b8607 Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Thu, 7 Jul 2022 19:23:06 +0000 Subject: [PATCH 151/505] Add annotate arguments as sqli sink --- .../security/cwe-089/ActiveRecordInjection.rb | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/ruby/ql/test/query-tests/security/cwe-089/ActiveRecordInjection.rb b/ruby/ql/test/query-tests/security/cwe-089/ActiveRecordInjection.rb index 9c314a82b34..4c8dfcca10d 100644 --- a/ruby/ql/test/query-tests/security/cwe-089/ActiveRecordInjection.rb +++ b/ruby/ql/test/query-tests/security/cwe-089/ActiveRecordInjection.rb @@ -137,3 +137,17 @@ class BazController < BarController Admin.delete_by(params[:admin_condition]) end end + +class AnnotatedController < ActionController::Base + def index + name = params[:user_name] + # GOOD: string literal arguments not controlled by user are safe for annotations + users = User.annotate("this is a safe annotation").find_by(user_name: name) + end + + def unsafe_action + name = params[:user_name] + # BAD: user input passed into annotations are vulnerable to SQLi + users = User.annotate("this is an unsafe annotation:#{params[:comment]}").find_by(user_name: name) + end +end From b4869158f2970dc462c78e2ebafcf90ea4b83f33 Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Thu, 7 Jul 2022 19:23:57 +0000 Subject: [PATCH 152/505] expand query tests for cwe-089 --- ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll | 5 +++++ .../test/query-tests/security/cwe-089/SqlInjection.expected | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll index cc63e64a083..b56304a657b 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll @@ -133,6 +133,11 @@ private Expr sqlFragmentArgument(MethodCall call) { or methodName = "reload" and result = call.getKeywordArgument("lock") + or + // Calls to `annotate` can be used to add block comments to SQL queries. These are potentially vulnerable to + // SQLi if user supplied input is passed in as an argument. + methodName = "annotate" and + result = call.getArgument(_) ) ) } diff --git a/ruby/ql/test/query-tests/security/cwe-089/SqlInjection.expected b/ruby/ql/test/query-tests/security/cwe-089/SqlInjection.expected index 6a9f5f771fb..dc814977cae 100644 --- a/ruby/ql/test/query-tests/security/cwe-089/SqlInjection.expected +++ b/ruby/ql/test/query-tests/security/cwe-089/SqlInjection.expected @@ -31,6 +31,8 @@ edges | ActiveRecordInjection.rb:99:11:99:17 | ...[...] : | ActiveRecordInjection.rb:104:20:104:32 | ... + ... | | ActiveRecordInjection.rb:137:21:137:26 | call to params : | ActiveRecordInjection.rb:137:21:137:44 | ...[...] : | | ActiveRecordInjection.rb:137:21:137:44 | ...[...] : | ActiveRecordInjection.rb:20:22:20:30 | condition : | +| ActiveRecordInjection.rb:151:59:151:64 | call to params : | ActiveRecordInjection.rb:151:59:151:74 | ...[...] : | +| ActiveRecordInjection.rb:151:59:151:74 | ...[...] : | ActiveRecordInjection.rb:151:27:151:76 | "this is an unsafe annotation:..." | nodes | ActiveRecordInjection.rb:8:25:8:28 | name : | semmle.label | name : | | ActiveRecordInjection.rb:8:31:8:34 | pass : | semmle.label | pass : | @@ -80,6 +82,9 @@ nodes | ActiveRecordInjection.rb:104:20:104:32 | ... + ... | semmle.label | ... + ... | | ActiveRecordInjection.rb:137:21:137:26 | call to params : | semmle.label | call to params : | | ActiveRecordInjection.rb:137:21:137:44 | ...[...] : | semmle.label | ...[...] : | +| ActiveRecordInjection.rb:151:27:151:76 | "this is an unsafe annotation:..." | semmle.label | "this is an unsafe annotation:..." | +| ActiveRecordInjection.rb:151:59:151:64 | call to params : | semmle.label | call to params : | +| ActiveRecordInjection.rb:151:59:151:74 | ...[...] : | semmle.label | ...[...] : | subpaths #select | ActiveRecordInjection.rb:10:33:10:67 | "name='#{...}' and pass='#{...}'" | ActiveRecordInjection.rb:70:23:70:28 | call to params : | ActiveRecordInjection.rb:10:33:10:67 | "name='#{...}' and pass='#{...}'" | This SQL query depends on $@. | ActiveRecordInjection.rb:70:23:70:28 | call to params | a user-provided value | @@ -99,3 +104,4 @@ subpaths | ActiveRecordInjection.rb:88:18:88:35 | ...[...] | ActiveRecordInjection.rb:88:18:88:23 | call to params : | ActiveRecordInjection.rb:88:18:88:35 | ...[...] | This SQL query depends on $@. | ActiveRecordInjection.rb:88:18:88:23 | call to params | a user-provided value | | ActiveRecordInjection.rb:92:21:92:35 | ...[...] | ActiveRecordInjection.rb:92:21:92:26 | call to params : | ActiveRecordInjection.rb:92:21:92:35 | ...[...] | This SQL query depends on $@. | ActiveRecordInjection.rb:92:21:92:26 | call to params | a user-provided value | | ActiveRecordInjection.rb:104:20:104:32 | ... + ... | ActiveRecordInjection.rb:98:10:98:15 | call to params : | ActiveRecordInjection.rb:104:20:104:32 | ... + ... | This SQL query depends on $@. | ActiveRecordInjection.rb:98:10:98:15 | call to params | a user-provided value | +| ActiveRecordInjection.rb:151:27:151:76 | "this is an unsafe annotation:..." | ActiveRecordInjection.rb:151:59:151:64 | call to params : | ActiveRecordInjection.rb:151:27:151:76 | "this is an unsafe annotation:..." | This SQL query depends on $@. | ActiveRecordInjection.rb:151:59:151:64 | call to params | a user-provided value | From 940254d2516be52e2aace4a27083d407b9bcb83d Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Thu, 7 Jul 2022 19:39:59 +0000 Subject: [PATCH 153/505] update framework tests --- .../library-tests/frameworks/ActiveRecord.expected | 4 ++++ ruby/ql/test/library-tests/frameworks/ActiveRecord.rb | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/ruby/ql/test/library-tests/frameworks/ActiveRecord.expected b/ruby/ql/test/library-tests/frameworks/ActiveRecord.expected index d8509f6a218..b416d853440 100644 --- a/ruby/ql/test/library-tests/frameworks/ActiveRecord.expected +++ b/ruby/ql/test/library-tests/frameworks/ActiveRecord.expected @@ -22,6 +22,7 @@ activeRecordSqlExecutionRanges | ActiveRecord.rb:46:20:46:32 | ... + ... | | ActiveRecord.rb:52:16:52:28 | "name #{...}" | | ActiveRecord.rb:56:20:56:39 | "username = #{...}" | +| ActiveRecord.rb:78:27:78:76 | "this is an unsafe annotation:..." | activeRecordModelClassMethodCalls | ActiveRecord.rb:2:3:2:17 | call to has_many | | ActiveRecord.rb:6:3:6:24 | call to belongs_to | @@ -44,6 +45,8 @@ activeRecordModelClassMethodCalls | ActiveRecord.rb:60:5:60:33 | call to find_by | | ActiveRecord.rb:62:5:62:34 | call to find | | ActiveRecord.rb:68:5:68:45 | call to delete_by | +| ActiveRecord.rb:74:13:74:54 | call to annotate | +| ActiveRecord.rb:78:13:78:77 | call to annotate | potentiallyUnsafeSqlExecutingMethodCall | ActiveRecord.rb:9:5:9:68 | call to find | | ActiveRecord.rb:19:5:19:25 | call to destroy_by | @@ -55,6 +58,7 @@ potentiallyUnsafeSqlExecutingMethodCall | ActiveRecord.rb:46:5:46:33 | call to delete_by | | ActiveRecord.rb:52:5:52:29 | call to order | | ActiveRecord.rb:56:7:56:40 | call to find_by | +| ActiveRecord.rb:78:13:78:77 | call to annotate | activeRecordModelInstantiations | ActiveRecord.rb:9:5:9:68 | call to find | ActiveRecord.rb:5:1:15:3 | User | | ActiveRecord.rb:13:5:13:40 | call to find_by | ActiveRecord.rb:1:1:3:3 | UserGroup | diff --git a/ruby/ql/test/library-tests/frameworks/ActiveRecord.rb b/ruby/ql/test/library-tests/frameworks/ActiveRecord.rb index 5045a5c2264..d25cbf901c3 100644 --- a/ruby/ql/test/library-tests/frameworks/ActiveRecord.rb +++ b/ruby/ql/test/library-tests/frameworks/ActiveRecord.rb @@ -68,3 +68,13 @@ class BazController < BarController Admin.delete_by(params[:admin_condition]) end end + +class AnnotatedController < ActionController::Base + def index + users = User.annotate("this is a safe annotation") + end + + def unsafe_action + users = User.annotate("this is an unsafe annotation:#{params[:comment]}") + end +end From 11e39aa0303d7207f5a03981fe69c651232828e6 Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Thu, 7 Jul 2022 21:40:16 +0000 Subject: [PATCH 154/505] Add changelog --- ruby/ql/lib/change-notes/released/0.3.1.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 ruby/ql/lib/change-notes/released/0.3.1.md diff --git a/ruby/ql/lib/change-notes/released/0.3.1.md b/ruby/ql/lib/change-notes/released/0.3.1.md new file mode 100644 index 00000000000..64efa15884a --- /dev/null +++ b/ruby/ql/lib/change-notes/released/0.3.1.md @@ -0,0 +1,5 @@ +## 0.3.1 + +### Minor Analysis Improvements + +- Calls to `ActiveRecord::Relation#annotate` have now been added to `ActiveRecordModelClass#sqlFragmentArgument` so that it can be used as a sink for queries like rb/sql-injection. \ No newline at end of file From bd50fd7f1e5413d1bd92ee95f43f15a46e63f7b0 Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Fri, 8 Jul 2022 17:20:41 +0000 Subject: [PATCH 155/505] format fix --- ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll index b56304a657b..142d1455ce4 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll @@ -135,7 +135,7 @@ private Expr sqlFragmentArgument(MethodCall call) { result = call.getKeywordArgument("lock") or // Calls to `annotate` can be used to add block comments to SQL queries. These are potentially vulnerable to - // SQLi if user supplied input is passed in as an argument. + // SQLi if user supplied input is passed in as an argument. methodName = "annotate" and result = call.getArgument(_) ) From 6aab970a9e86ff0dd0aa1660c489d4d0bcba525c Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Fri, 8 Jul 2022 18:32:54 +0000 Subject: [PATCH 156/505] refactor query to use cfg and dataflow --- .../ManuallyCheckHttpVerb.ql | 96 +++++++++++++------ .../ExampleController.rb | 11 --- .../ManuallyCheckHttpVerb.expected | 0 .../ManuallyCheckHttpVerb.qlref | 0 .../manually-check-http-verb/NotController.rb | 17 ++++ .../manually-check-http-verb/NotController.rb | 28 ------ 6 files changed, 85 insertions(+), 67 deletions(-) rename ruby/ql/test/query-tests/{security => experimental}/manually-check-http-verb/ExampleController.rb (70%) rename ruby/ql/test/query-tests/{security => experimental}/manually-check-http-verb/ManuallyCheckHttpVerb.expected (100%) rename ruby/ql/test/query-tests/{security => experimental}/manually-check-http-verb/ManuallyCheckHttpVerb.qlref (100%) create mode 100644 ruby/ql/test/query-tests/experimental/manually-check-http-verb/NotController.rb delete mode 100644 ruby/ql/test/query-tests/security/manually-check-http-verb/NotController.rb diff --git a/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql b/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql index a006587a13e..5e1db6f0de7 100644 --- a/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql +++ b/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql @@ -11,43 +11,83 @@ import ruby import codeql.ruby.DataFlow +import codeql.ruby.controlflow.CfgNodes +import codeql.ruby.frameworks.ActionController -class CheckNotGetRequest extends ConditionalExpr { - CheckNotGetRequest() { this.getCondition() instanceof CheckGetRequest } +// any `request` calls in an action method +class Request extends DataFlow::CallNode { + Request() { + this.getMethodName() = "request" and + this.asExpr().getExpr() instanceof ActionControllerActionMethod + } } -class CheckGetRequest extends MethodCall { - CheckGetRequest() { this.getMethodName() = "get?" } +// `request.request_method` +class RequestRequestMethod extends DataFlow::CallNode { + RequestRequestMethod() { + this.getMethodName() = "request_method" and + any(Request r).flowsTo(this.getReceiver()) + } } -class ControllerClass extends ModuleBase { - ControllerClass() { this.getModule().getSuperClass+().toString() = "ApplicationController" } +// `request.method` +class RequestMethod extends DataFlow::CallNode { + RequestMethod() { + this.getMethodName() = "method" and + any(Request r).flowsTo(this.getReceiver()) + } } -class CheckGetFromEnv extends AstNode { - CheckGetFromEnv() { - // is this node an instance of `env["REQUEST_METHOD"] - this instanceof GetRequestMethodFromEnv and - // check if env["REQUEST_METHOD"] is compared to GET - exists(EqualityOperation eq | eq.getAChild() = this | - eq.getAChild().(StringLiteral).toString() = "GET" +// `request.raw_request_method` +class RequestRawRequestMethod extends DataFlow::CallNode { + RequestRawRequestMethod() { + this.getMethodName() = "raw_request_method" and + any(Request r).flowsTo(this.getReceiver()) + } +} + +// `request.request_method_symbol` +class RequestRequestMethodSymbol extends DataFlow::CallNode { + RequestRequestMethodSymbol() { + this.getMethodName() = "request_method_symbol" and + any(Request r).flowsTo(this.getReceiver()) + } +} + +// `request.get?` +class RequestGet extends DataFlow::CallNode { + RequestGet() { + this.getMethodName() = "get?" and + any(Request r).flowsTo(this.getReceiver()) + } +} + +// A conditional expression where the condition uses `request.method`, `request.request_method`, `request.raw_request_method`, `request.request_method_symbol`, or `request.get?` in some way. +// e.g. +// ``` +// r = request.request_method +// if r == "GET" +// ... +// ``` +class RequestMethodConditional extends DataFlow::Node { + RequestMethodConditional() { + // We have to cast the dataflow node down to a specific CFG node (`ExprNodes::ConditionalExprCfgNode`) to be able to call `getCondition()`. + // We then find the dataflow node corresponding to the condition CFG node, + // and filter for just nodes where a request method accessor value flows to them. + exists(DataFlow::Node conditionNode | + conditionNode.asExpr() = this.asExpr().(ExprNodes::ConditionalExprCfgNode).getCondition() + | + ( + any(RequestMethod r).flowsTo(conditionNode) or + any(RequestRequestMethod r).flowsTo(conditionNode) or + any(RequestRawRequestMethod r).flowsTo(conditionNode) or + any(RequestRequestMethodSymbol r).flowsTo(conditionNode) or + any(RequestGet r).flowsTo(conditionNode) + ) ) } } -class GetRequestMethodFromEnv extends ElementReference { - GetRequestMethodFromEnv() { - this.getAChild+().toString() = "REQUEST_METHOD" and - this.getAChild+().toString() = "env" - } -} - -from AstNode node -where - ( - node instanceof CheckNotGetRequest or - node instanceof CheckGetFromEnv - ) and - node.getEnclosingModule() instanceof ControllerClass -select node, +from RequestMethodConditional req +select req, "Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods." diff --git a/ruby/ql/test/query-tests/security/manually-check-http-verb/ExampleController.rb b/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ExampleController.rb similarity index 70% rename from ruby/ql/test/query-tests/security/manually-check-http-verb/ExampleController.rb rename to ruby/ql/test/query-tests/experimental/manually-check-http-verb/ExampleController.rb index c3e913367b8..7e4ab4a1a77 100644 --- a/ruby/ql/test/query-tests/security/manually-check-http-verb/ExampleController.rb +++ b/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ExampleController.rb @@ -3,17 +3,6 @@ class ExampleController < ActionController::Base def example_action if request.get? Example.find(params[:example_id]) - elsif request.post? - Example.new(params[:example_name], params[:example_details]) - elsif request.delete? - example = Example.find(params[:example_id]) - example.delete - elsif request.put? - Example.upsert(params[:example_name], params[:example_details]) - elsif request.path? - Example.update(params[:example_name], params[:example_details]) - elsif request.head? - "This is the endpoint for the Example resource." end end end diff --git a/ruby/ql/test/query-tests/security/manually-check-http-verb/ManuallyCheckHttpVerb.expected b/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.expected similarity index 100% rename from ruby/ql/test/query-tests/security/manually-check-http-verb/ManuallyCheckHttpVerb.expected rename to ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.expected diff --git a/ruby/ql/test/query-tests/security/manually-check-http-verb/ManuallyCheckHttpVerb.qlref b/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.qlref similarity index 100% rename from ruby/ql/test/query-tests/security/manually-check-http-verb/ManuallyCheckHttpVerb.qlref rename to ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.qlref diff --git a/ruby/ql/test/query-tests/experimental/manually-check-http-verb/NotController.rb b/ruby/ql/test/query-tests/experimental/manually-check-http-verb/NotController.rb new file mode 100644 index 00000000000..78e194245e2 --- /dev/null +++ b/ruby/ql/test/query-tests/experimental/manually-check-http-verb/NotController.rb @@ -0,0 +1,17 @@ +# There should be no hits from this class because it does not inherit from ActionController +class NotAController + def example_action + if request.get? + Example.find(params[:example_id]) + end + end + + def resource_action + case env['REQUEST_METHOD'] + when "GET" + Resource.find(params[:id]) + when "POST" + Resource.new(params[:id], params[:details]) + end + end +end \ No newline at end of file diff --git a/ruby/ql/test/query-tests/security/manually-check-http-verb/NotController.rb b/ruby/ql/test/query-tests/security/manually-check-http-verb/NotController.rb deleted file mode 100644 index 81a73d72410..00000000000 --- a/ruby/ql/test/query-tests/security/manually-check-http-verb/NotController.rb +++ /dev/null @@ -1,28 +0,0 @@ -# There should be no hits from this class because it does not inherit from ActionController -class NotAController - def example_action - if request.get? - Example.find(params[:example_id]) - elsif request.post? - Example.new(params[:example_name], params[:example_details]) - elsif request.delete? - example = Example.find(params[:example_id]) - example.delete - elsif request.put? - Example.upsert(params[:example_name], params[:example_details]) - elsif request.path? - Example.update(params[:example_name], params[:example_details]) - elsif request.head? - "This is the endpoint for the Example resource." - end - end - - def resource_action - case env['REQUEST_METHOD'] - when "GET" - Resource.find(params[:id]) - when "POST" - Resource.new(params[:id], params[:details]) - end - end -end \ No newline at end of file From 96e66c4a504cdd473f52e8b50140f6261d3a6542 Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Fri, 8 Jul 2022 18:39:04 +0000 Subject: [PATCH 157/505] move tests --- .../{security => experimental}/weak-params/WeakParams.expected | 0 .../{security => experimental}/weak-params/WeakParams.qlref | 0 .../{security => experimental}/weak-params/WeakParams.rb | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename ruby/ql/test/query-tests/{security => experimental}/weak-params/WeakParams.expected (100%) rename ruby/ql/test/query-tests/{security => experimental}/weak-params/WeakParams.qlref (100%) rename ruby/ql/test/query-tests/{security => experimental}/weak-params/WeakParams.rb (100%) diff --git a/ruby/ql/test/query-tests/security/weak-params/WeakParams.expected b/ruby/ql/test/query-tests/experimental/weak-params/WeakParams.expected similarity index 100% rename from ruby/ql/test/query-tests/security/weak-params/WeakParams.expected rename to ruby/ql/test/query-tests/experimental/weak-params/WeakParams.expected diff --git a/ruby/ql/test/query-tests/security/weak-params/WeakParams.qlref b/ruby/ql/test/query-tests/experimental/weak-params/WeakParams.qlref similarity index 100% rename from ruby/ql/test/query-tests/security/weak-params/WeakParams.qlref rename to ruby/ql/test/query-tests/experimental/weak-params/WeakParams.qlref diff --git a/ruby/ql/test/query-tests/security/weak-params/WeakParams.rb b/ruby/ql/test/query-tests/experimental/weak-params/WeakParams.rb similarity index 100% rename from ruby/ql/test/query-tests/security/weak-params/WeakParams.rb rename to ruby/ql/test/query-tests/experimental/weak-params/WeakParams.rb From 5d3232c614b591cf234c6e8ebc0dc2e099228dcc Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Fri, 8 Jul 2022 18:53:24 +0000 Subject: [PATCH 158/505] refactor to use data flow --- .../experimental/weak-params/WeakParams.ql | 79 ++++++------------- .../experimental/weak-params/WeakParams.rb | 2 +- 2 files changed, 23 insertions(+), 58 deletions(-) diff --git a/ruby/ql/src/experimental/weak-params/WeakParams.ql b/ruby/ql/src/experimental/weak-params/WeakParams.ql index f9af2e5c08c..6ea73aa42de 100644 --- a/ruby/ql/src/experimental/weak-params/WeakParams.ql +++ b/ruby/ql/src/experimental/weak-params/WeakParams.ql @@ -11,45 +11,41 @@ */ import ruby +import codeql.ruby.Concepts import codeql.ruby.DataFlow import codeql.ruby.TaintTracking +import codeql.ruby.frameworks.ActionController import DataFlow::PathGraph /** - * A direct parameters reference that happens outside of a strong params method but inside - * of a controller class + * A call to `request` in an ActionController controller class. + * This probably refers to the incoming HTTP request object. */ -class WeakParams extends Expr { - WeakParams() { - ( - allParamsAccess(this) or - this instanceof ParamsReference - ) and - this.getEnclosingModule() instanceof ControllerClass and - not this.getEnclosingMethod() instanceof StrongParamsMethod +class ActionControllerRequest extends DataFlow::Node { + ActionControllerRequest() { + exists(DataFlow::CallNode c | + c.asExpr().getExpr().getEnclosingModule() instanceof ActionControllerControllerClass and + c.getMethodName() = "request" + | + c.flowsTo(this) + ) } } /** - * A controller class, which extendsd `ApplicationController` + * A direct parameters reference that happens inside a controller class. */ -class ControllerClass extends ModuleBase { - ControllerClass() { this.getModule().getSuperClass+().toString() = "ApplicationController" } -} - -/** - * A method that follows the strong params naming convention - */ -class StrongParamsMethod extends Method { - StrongParamsMethod() { this.getName().regexpMatch(".*_params") } +class WeakParams extends DataFlow::CallNode { + WeakParams() { + this.getReceiver() instanceof ActionControllerRequest and + allParamsAccess(this.asExpr().getExpr()) + } } /** * Holds call to a method that exposes or accesses all parameters from an inbound HTTP request */ predicate allParamsAccess(MethodCall call) { - call.getMethodName() = "expose_all" or - call.getMethodName() = "original_hash" or call.getMethodName() = "path_parametes" or call.getMethodName() = "query_parameters" or call.getMethodName() = "request_parameters" or @@ -57,51 +53,20 @@ predicate allParamsAccess(MethodCall call) { call.getMethodName() = "POST" } -/** - * A reference to an element in the `params` object - */ -class ParamsReference extends ElementReference { - ParamsReference() { this.getAChild().toString() = "params" } -} - -/** - * A Model or ViewModel classes with a base class of `ViewModel`, `ApplicationRecord` or includes `ActionModel::Model`, - * which are required to support the strong parameters pattern - */ -class ModelClass extends ModuleBase { - ModelClass() { - this.getModule().getSuperClass+().toString() = "ViewModel" or - this.getModule().getSuperClass+().toString() = "ApplicationRecord" or - this.getModule().getSuperClass+().getAnIncludedModule().toString() = "ActionModel::Model" - } -} - -/** - * A DataFlow::Node representation that corresponds to any argument passed into a method call - * where the receiver is an instance of ModelClass - */ -class ModelClassMethodArgument extends DataFlow::Node { - - ModelClassMethodArgument() { - exists( DataFlow::CallNode call | this = call.getArgument(_) | - call.getExprNode().getNode().getParent+() instanceof ModelClass ) - } -} - /** * A Taint tracking config where the source is a weak params access in a controller and the sink * is a method call of a model class */ class Configuration extends TaintTracking::Configuration { - Configuration() { this = "Configuration" } + Configuration() { this = "WeakParamsConfiguration" } - override predicate isSource(DataFlow::Node node) { node.asExpr().getNode() instanceof WeakParams } + override predicate isSource(DataFlow::Node node) { node instanceof WeakParams } // the sink is an instance of a Model class that receives a method call - override predicate isSink(DataFlow::Node node) { node instanceof ModelClassMethodArgument } + override predicate isSink(DataFlow::Node node) { node = any(PersistentWriteAccess a).getValue() } } from Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink where config.hasFlowPath(source, sink) -select sink.getNode().(ModelClassMethodArgument), source, sink, +select sink.getNode(), source, sink, "By exposing all keys in request parameters or by blindy accessing them, unintended parameters could be used and lead to mass-assignment or have other unexpected side-effects. It is safer to follow the 'strong parameters' pattern in Rails, which is outlined here: https://api.rubyonrails.org/classes/ActionController/StrongParameters.html" diff --git a/ruby/ql/test/query-tests/experimental/weak-params/WeakParams.rb b/ruby/ql/test/query-tests/experimental/weak-params/WeakParams.rb index a2fedd6ef26..81d57239f29 100644 --- a/ruby/ql/test/query-tests/experimental/weak-params/WeakParams.rb +++ b/ruby/ql/test/query-tests/experimental/weak-params/WeakParams.rb @@ -8,7 +8,7 @@ class TestController < ActionController::Base end def update - TestObect.update(object_params) + TestObject.update(object_params) end # From e8e8da1b3189adba08fe944db5df482287b52a49 Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Fri, 8 Jul 2022 19:01:01 +0000 Subject: [PATCH 159/505] fix lib test expect for ActionController --- .../test/library-tests/frameworks/ActionController.expected | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ruby/ql/test/library-tests/frameworks/ActionController.expected b/ruby/ql/test/library-tests/frameworks/ActionController.expected index d306f09b64b..52ab15995c7 100644 --- a/ruby/ql/test/library-tests/frameworks/ActionController.expected +++ b/ruby/ql/test/library-tests/frameworks/ActionController.expected @@ -2,6 +2,7 @@ actionControllerControllerClasses | ActiveRecord.rb:23:1:39:3 | FooController | | ActiveRecord.rb:41:1:64:3 | BarController | | ActiveRecord.rb:66:1:70:3 | BazController | +| ActiveRecord.rb:72:1:80:3 | AnnotatedController | | app/controllers/comments_controller.rb:1:1:7:3 | CommentsController | | app/controllers/foo/bars_controller.rb:3:1:39:3 | BarsController | | app/controllers/photos_controller.rb:1:1:4:3 | PhotosController | @@ -12,6 +13,8 @@ actionControllerActionMethods | ActiveRecord.rb:42:3:47:5 | some_other_request_handler | | ActiveRecord.rb:49:3:63:5 | safe_paths | | ActiveRecord.rb:67:3:69:5 | yet_another_handler | +| ActiveRecord.rb:73:3:75:5 | index | +| ActiveRecord.rb:77:3:79:5 | unsafe_action | | app/controllers/comments_controller.rb:2:3:3:5 | index | | app/controllers/comments_controller.rb:5:3:6:5 | show | | app/controllers/foo/bars_controller.rb:5:3:7:5 | index | @@ -38,6 +41,7 @@ paramsCalls | ActiveRecord.rb:59:12:59:17 | call to params | | ActiveRecord.rb:62:15:62:20 | call to params | | ActiveRecord.rb:68:21:68:26 | call to params | +| ActiveRecord.rb:78:59:78:64 | call to params | | app/controllers/foo/bars_controller.rb:13:21:13:26 | call to params | | app/controllers/foo/bars_controller.rb:14:10:14:15 | call to params | | app/controllers/foo/bars_controller.rb:21:21:21:26 | call to params | @@ -57,6 +61,7 @@ paramsSources | ActiveRecord.rb:59:12:59:17 | call to params | | ActiveRecord.rb:62:15:62:20 | call to params | | ActiveRecord.rb:68:21:68:26 | call to params | +| ActiveRecord.rb:78:59:78:64 | call to params | | app/controllers/foo/bars_controller.rb:13:21:13:26 | call to params | | app/controllers/foo/bars_controller.rb:14:10:14:15 | call to params | | app/controllers/foo/bars_controller.rb:21:21:21:26 | call to params | From 7c3cadc9b6c7b2556f9a76b469b3f36f97133415 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Mon, 11 Jul 2022 10:40:45 +0200 Subject: [PATCH 160/505] Swift: extract `OpenedArchetypeType` --- swift/extractor/visitors/TypeVisitor.cpp | 7 +++++- swift/extractor/visitors/TypeVisitor.h | 1 + .../OpenedArchetypeType/MISSING_SOURCE.txt | 4 --- .../OpenedArchetypeType.expected | 1 + .../OpenedArchetypeType.ql | 13 ++++++++++ .../OpenedArchetypeType_getProtocol.expected | 2 ++ .../OpenedArchetypeType_getProtocol.ql | 7 ++++++ ...OpenedArchetypeType_getSuperclass.expected | 1 + .../OpenedArchetypeType_getSuperclass.ql | 7 ++++++ .../opened_archetypes.swift | 25 +++++++++++++++++++ 10 files changed, 63 insertions(+), 5 deletions(-) delete mode 100644 swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/MISSING_SOURCE.txt create mode 100644 swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType.expected create mode 100644 swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType.ql create mode 100644 swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType_getProtocol.expected create mode 100644 swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType_getProtocol.ql create mode 100644 swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType_getSuperclass.expected create mode 100644 swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType_getSuperclass.ql create mode 100644 swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/opened_archetypes.swift diff --git a/swift/extractor/visitors/TypeVisitor.cpp b/swift/extractor/visitors/TypeVisitor.cpp index 4b6733312d3..945ef5693ce 100644 --- a/swift/extractor/visitors/TypeVisitor.cpp +++ b/swift/extractor/visitors/TypeVisitor.cpp @@ -367,5 +367,10 @@ codeql::BuiltinVectorType TypeVisitor::translateBuiltinVectorType( const swift::BuiltinVectorType& type) { return createTypeEntry(type); } - +codeql::OpenedArchetypeType TypeVisitor::translateOpenedArchetypeType( + const swift::OpenedArchetypeType& type) { + auto entry = createTypeEntry(type); + fillArchetypeType(type, entry); + return entry; +} } // namespace codeql diff --git a/swift/extractor/visitors/TypeVisitor.h b/swift/extractor/visitors/TypeVisitor.h index 77ae8ee13bf..61ec7796cf9 100644 --- a/swift/extractor/visitors/TypeVisitor.h +++ b/swift/extractor/visitors/TypeVisitor.h @@ -68,6 +68,7 @@ class TypeVisitor : public TypeVisitorBase { codeql::BuiltinUnsafeValueBufferType translateBuiltinUnsafeValueBufferType( const swift::BuiltinUnsafeValueBufferType& type); codeql::BuiltinVectorType translateBuiltinVectorType(const swift::BuiltinVectorType& type); + codeql::OpenedArchetypeType translateOpenedArchetypeType(const swift::OpenedArchetypeType& type); private: void fillType(const swift::TypeBase& type, codeql::Type& entry); diff --git a/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType.expected b/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType.expected new file mode 100644 index 00000000000..f6c15bca419 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType.expected @@ -0,0 +1 @@ +| C & P1 & P2 | getName: | C & P1 & P2 | getCanonicalType: | C & P1 & P2 | getInterfaceType: | \u03c4_0_0 | diff --git a/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType.ql b/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType.ql new file mode 100644 index 00000000000..b558c08f666 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType.ql @@ -0,0 +1,13 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from OpenedArchetypeType x, string getName, Type getCanonicalType, Type getInterfaceType +where + toBeTested(x) and + not x.isUnknown() and + getName = x.getName() and + getCanonicalType = x.getCanonicalType() and + getInterfaceType = x.getInterfaceType() +select x, "getName:", getName, "getCanonicalType:", getCanonicalType, "getInterfaceType:", + getInterfaceType diff --git a/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType_getProtocol.expected b/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType_getProtocol.expected new file mode 100644 index 00000000000..5899ea9308a --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType_getProtocol.expected @@ -0,0 +1,2 @@ +| C & P1 & P2 | 0 | opened_archetypes.swift:3:1:3:14 | P1 | +| C & P1 & P2 | 1 | opened_archetypes.swift:9:1:9:14 | P2 | diff --git a/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType_getProtocol.ql b/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType_getProtocol.ql new file mode 100644 index 00000000000..58fed5dda7b --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType_getProtocol.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from OpenedArchetypeType x, int index +where toBeTested(x) and not x.isUnknown() +select x, index, x.getProtocol(index) diff --git a/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType_getSuperclass.expected b/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType_getSuperclass.expected new file mode 100644 index 00000000000..be1cb7dcb05 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType_getSuperclass.expected @@ -0,0 +1 @@ +| C & P1 & P2 | C | diff --git a/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType_getSuperclass.ql b/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType_getSuperclass.ql new file mode 100644 index 00000000000..46f00a08c88 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType_getSuperclass.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from OpenedArchetypeType x +where toBeTested(x) and not x.isUnknown() +select x, x.getSuperclass() diff --git a/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/opened_archetypes.swift b/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/opened_archetypes.swift new file mode 100644 index 00000000000..93d58b6163f --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/opened_archetypes.swift @@ -0,0 +1,25 @@ +// code inspired by https://github.com/apple/swift-evolution/blob/main/proposals/0352-implicit-open-existentials.md + +protocol P1 {} + +func isFoo(_: T) -> Bool { + return true +} + +protocol P2 {} + +class C {} + +// will be ok with swift 5.7 +// func test(value: any P1 & P2 & C) -> Bool { return isFoo(value) } + +extension P1 { + var isFooMember: Bool { + isFoo(self) + } +} + + +func testMember(value: any P1 & P2 & C) -> Bool { + return value.isFooMember // here the existential type is implicitly "opened" +} From 7d5dd384c374457846a9d1de5f7d8b95947f19ec Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Mon, 11 Jul 2022 10:59:00 +0200 Subject: [PATCH 161/505] Swift: extract `UnresolvedPatternExpr` --- swift/codegen/schema.yml | 2 ++ swift/extractor/visitors/ExprVisitor.cpp | 6 ++++++ swift/extractor/visitors/ExprVisitor.h | 5 +++++ swift/extractor/visitors/VisitorBase.h | 2 +- .../ql/lib/codeql/swift/generated/GetImmediateParent.qll | 2 ++ .../codeql/swift/generated/expr/UnresolvedPatternExpr.qll | 8 ++++++++ swift/ql/lib/swift.dbscheme | 3 ++- 7 files changed, 26 insertions(+), 2 deletions(-) diff --git a/swift/codegen/schema.yml b/swift/codegen/schema.yml index 01bb0c2feba..96da8c96936 100644 --- a/swift/codegen/schema.yml +++ b/swift/codegen/schema.yml @@ -561,6 +561,8 @@ UnresolvedMemberExpr: UnresolvedPatternExpr: _extends: Expr _pragma: qltest_skip # we should really never extract these + _children: + sub_pattern: Pattern UnresolvedSpecializeExpr: _extends: Expr diff --git a/swift/extractor/visitors/ExprVisitor.cpp b/swift/extractor/visitors/ExprVisitor.cpp index e6aabef4cea..90ca0a168ee 100644 --- a/swift/extractor/visitors/ExprVisitor.cpp +++ b/swift/extractor/visitors/ExprVisitor.cpp @@ -667,5 +667,11 @@ void ExprVisitor::emitLookupExpr(const swift::LookupExpr* expr, TrapLabel { codeql::BridgeFromObjCExpr translateBridgeFromObjCExpr(const swift::BridgeFromObjCExpr& expr); codeql::DotSelfExpr translateDotSelfExpr(const swift::DotSelfExpr& expr); codeql::ErrorExpr translateErrorExpr(const swift::ErrorExpr& expr); + // following requires non-const because: + // * `swift::UnresolvedPatternExpr::getSubPattern` gives `const swift::Pattern*` on const refs + // * `swift::ASTVisitor` only visits non-const pointers + // either we accept this, or we fix constness by providing our own const visiting in VisitorBase + codeql::UnresolvedPatternExpr translateUnresolvedPatternExpr(swift::UnresolvedPatternExpr& expr); private: void fillAbstractClosureExpr(const swift::AbstractClosureExpr& expr, diff --git a/swift/extractor/visitors/VisitorBase.h b/swift/extractor/visitors/VisitorBase.h index 8402e8924af..b835492d00d 100644 --- a/swift/extractor/visitors/VisitorBase.h +++ b/swift/extractor/visitors/VisitorBase.h @@ -29,7 +29,7 @@ class VisitorBase { public: \ void visit##CLASS##KIND(swift::CLASS##KIND* e) { \ using TranslateResult = std::invoke_result_t; \ + CrtpSubclass, swift::CLASS##KIND&>; \ constexpr bool hasTranslateImplementation = !std::is_same_v; \ if constexpr (hasTranslateImplementation) { \ dispatcher_.emit(static_cast(this)->translate##CLASS##KIND(*e)); \ diff --git a/swift/ql/lib/codeql/swift/generated/GetImmediateParent.qll b/swift/ql/lib/codeql/swift/generated/GetImmediateParent.qll index aaf3dec16bd..4cecd1a3ce6 100644 --- a/swift/ql/lib/codeql/swift/generated/GetImmediateParent.qll +++ b/swift/ql/lib/codeql/swift/generated/GetImmediateParent.qll @@ -144,6 +144,8 @@ Element getAnImmediateChild(Element e) { or unresolved_dot_exprs(e, x, _) or + unresolved_pattern_exprs(e, x) + or vararg_expansion_exprs(e, x) or binding_patterns(e, x) diff --git a/swift/ql/lib/codeql/swift/generated/expr/UnresolvedPatternExpr.qll b/swift/ql/lib/codeql/swift/generated/expr/UnresolvedPatternExpr.qll index 8d03b2079aa..ae62f2df23b 100644 --- a/swift/ql/lib/codeql/swift/generated/expr/UnresolvedPatternExpr.qll +++ b/swift/ql/lib/codeql/swift/generated/expr/UnresolvedPatternExpr.qll @@ -1,6 +1,14 @@ // generated by codegen/codegen.py import codeql.swift.elements.expr.Expr +import codeql.swift.elements.pattern.Pattern class UnresolvedPatternExprBase extends @unresolved_pattern_expr, Expr { override string getAPrimaryQlClass() { result = "UnresolvedPatternExpr" } + + Pattern getSubPattern() { + exists(Pattern x | + unresolved_pattern_exprs(this, x) and + result = x.resolve() + ) + } } diff --git a/swift/ql/lib/swift.dbscheme b/swift/ql/lib/swift.dbscheme index 83ee8412131..c12e00e028d 100644 --- a/swift/ql/lib/swift.dbscheme +++ b/swift/ql/lib/swift.dbscheme @@ -1141,7 +1141,8 @@ unresolved_member_exprs( //dir=expr ); unresolved_pattern_exprs( //dir=expr - unique int id: @unresolved_pattern_expr + unique int id: @unresolved_pattern_expr, + int sub_pattern: @pattern ref ); unresolved_specialize_exprs( //dir=expr From 6b2154eb8b464b58e44f3416824fe0bdd2285f35 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Mon, 11 Jul 2022 11:54:48 +0200 Subject: [PATCH 162/505] C++: Add tests for `AnalysedExpr::isNullCheck` and `AnalysedExpr::isValidCheck` --- .../controlflow/nullness/nullness.expected | 15 ++++++++++++ .../controlflow/nullness/nullness.ql | 9 ++++++++ .../controlflow/nullness/test.cpp | 23 +++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 cpp/ql/test/library-tests/controlflow/nullness/nullness.expected create mode 100644 cpp/ql/test/library-tests/controlflow/nullness/nullness.ql create mode 100644 cpp/ql/test/library-tests/controlflow/nullness/test.cpp diff --git a/cpp/ql/test/library-tests/controlflow/nullness/nullness.expected b/cpp/ql/test/library-tests/controlflow/nullness/nullness.expected new file mode 100644 index 00000000000..db5c795fd5b --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/nullness/nullness.expected @@ -0,0 +1,15 @@ +| test.cpp:9:9:9:9 | v | test.cpp:5:13:5:13 | v | is not null | is valid | +| test.cpp:10:9:10:10 | ! ... | test.cpp:5:13:5:13 | v | is null | is not valid | +| test.cpp:11:9:11:14 | ... == ... | test.cpp:5:13:5:13 | v | is null | is not valid | +| test.cpp:12:9:12:17 | ... == ... | test.cpp:5:13:5:13 | v | is not null | is valid | +| test.cpp:13:9:13:14 | ... != ... | test.cpp:5:13:5:13 | v | is not null | is valid | +| test.cpp:14:9:14:17 | ... != ... | test.cpp:5:13:5:13 | v | is null | is not valid | +| test.cpp:15:8:15:23 | call to __builtin_expect | test.cpp:5:13:5:13 | v | is not null | is valid | +| test.cpp:16:8:16:23 | call to __builtin_expect | test.cpp:5:13:5:13 | v | is null | is not valid | +| test.cpp:17:9:17:17 | ... && ... | test.cpp:5:13:5:13 | v | is not null | is valid | +| test.cpp:18:9:18:17 | ... && ... | test.cpp:5:13:5:13 | v | is not null | is not valid | +| test.cpp:19:9:19:18 | ... && ... | test.cpp:5:13:5:13 | v | is null | is not valid | +| test.cpp:20:9:20:18 | ... && ... | test.cpp:5:13:5:13 | v | is not null | is not valid | +| test.cpp:21:9:21:14 | ... = ... | test.cpp:5:13:5:13 | v | is null | is not valid | +| test.cpp:21:9:21:14 | ... = ... | test.cpp:7:10:7:10 | b | is not null | is valid | +| test.cpp:22:17:22:17 | b | test.cpp:7:10:7:10 | b | is not null | is valid | diff --git a/cpp/ql/test/library-tests/controlflow/nullness/nullness.ql b/cpp/ql/test/library-tests/controlflow/nullness/nullness.ql new file mode 100644 index 00000000000..ed1ba15aa2b --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/nullness/nullness.ql @@ -0,0 +1,9 @@ +import cpp + +from AnalysedExpr a, LocalScopeVariable v, string isNullCheck, string isValidCheck +where + a.getParent() instanceof IfStmt and + v.getAnAccess().getEnclosingStmt() = a.getParent() and + (if a.isNullCheck(v) then isNullCheck = "is null" else isNullCheck = "is not null") and + (if a.isValidCheck(v) then isValidCheck = "is valid" else isValidCheck = "is not valid") +select a, v, isNullCheck, isValidCheck diff --git a/cpp/ql/test/library-tests/controlflow/nullness/test.cpp b/cpp/ql/test/library-tests/controlflow/nullness/test.cpp new file mode 100644 index 00000000000..03369c811d5 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/nullness/test.cpp @@ -0,0 +1,23 @@ +// semmle-extractor-options: -std=c++17 + +long __builtin_expect(long); + +void f(int *v) { + int *w; + bool b; + + if (v) {} + if (!v) {} + if (v == 0) {} + if ((!v) == 0) {} + if (v != 0) {} + if ((!v) != 0) {} + if(__builtin_expect((long)v)) {} + if(__builtin_expect((long)!v)) {} + if (true && v) {} + if (v && true) {} + if (true && !v) {} + if (!v && true) {} + if (b = !v) {} + if (b = !v; b) {} +} From 74641ccfee54e6c7fd824b9ffc9726cd6f3c2291 Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Mon, 11 Jul 2022 11:01:19 +0100 Subject: [PATCH 163/505] Simplify test for no-arg constructor --- .../CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql b/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql index 7f1ec039d20..a109e945343 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql @@ -24,7 +24,7 @@ predicate isCreatingOutdatedAzureClientSideEncryptionObject(Call call, Class c) ( type = "EncryptedBlobClientBuilder" and package = "com.azure.storage.blob.specialized.cryptography" and - not exists(Expr e | e = call.getArgument(0)) + constructor.hasNoParameters() or type = "BlobEncryptionPolicy" and package = "com.microsoft.azure.storage.blob" ) From 9ed7aa9fae85df1a40c65f6bcd89fcd52ea45926 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 11 Jul 2022 12:52:23 +0200 Subject: [PATCH 164/505] exclude variables in .vue files form js/unused-local-variable --- javascript/ql/src/Declarations/UnusedVariable.ql | 3 +++ 1 file changed, 3 insertions(+) diff --git a/javascript/ql/src/Declarations/UnusedVariable.ql b/javascript/ql/src/Declarations/UnusedVariable.ql index 254c8c206b1..f678c7d5b19 100644 --- a/javascript/ql/src/Declarations/UnusedVariable.ql +++ b/javascript/ql/src/Declarations/UnusedVariable.ql @@ -144,6 +144,9 @@ predicate whitelisted(UnusedLocal v) { // exclude variables mentioned in JSDoc comments in externs mentionedInJSDocComment(v) or + // the attributes in .vue files are not extracted, so we can get false positives in those. + v.getADeclaration().getFile().getExtension() = "vue" + or // exclude variables used to filter out unwanted properties isPropertyFilter(v) or From aa07600f5a5694b6739ae2fa15474fd7fc99b4ec Mon Sep 17 00:00:00 2001 From: Ian Lynagh Date: Fri, 1 Jul 2022 12:20:26 +0100 Subject: [PATCH 165/505] Java: Update stats --- java/ql/lib/config/semmlecode.dbscheme.stats | 4260 +++++++++--------- 1 file changed, 2165 insertions(+), 2095 deletions(-) diff --git a/java/ql/lib/config/semmlecode.dbscheme.stats b/java/ql/lib/config/semmlecode.dbscheme.stats index 2fc1431a73f..0490619a996 100644 --- a/java/ql/lib/config/semmlecode.dbscheme.stats +++ b/java/ql/lib/config/semmlecode.dbscheme.stats @@ -1,20 +1,20 @@ - - @diagnostic - 634718 - - - @externalDataElement - 1 - @javacompilation 8629 @kotlincompilation - 6824 + 6822 + + + @diagnostic + 624933 + + + @externalDataElement + 1 @duplication @@ -26,27 +26,31 @@ @file - 8020653 + 8017700 @folder - 1280356 + 1279884 @package - 612878 + 612652 @primitive - 12284 + 12280 @modifier - 12284 + 13644 + + + @errortype + 1 @class - 12579704 + 12579165 @kt_nullable_type @@ -54,7 +58,7 @@ @kt_notnull_type - 193427 + 194340 @kt_type_alias @@ -66,27 +70,27 @@ @fielddecl - 399940 + 399793 @field - 27583622 + 27573466 @constructor - 6889080 + 6886544 @method - 93482380 - - - @location_default - 431212495 + 93447961 @param - 101137218 + 101099980 + + + @location_default + 431053728 @exception @@ -94,19 +98,19 @@ @typevariable - 5118694 + 5116810 @wildcard - 3116261 + 3637710 @typebound - 3872463 + 4393634 @array - 1119287 + 1118875 @import @@ -114,7 +118,7 @@ @block - 846290 + 845979 @ifstmt @@ -130,7 +134,7 @@ @whilestmt - 19743 + 19739 @dostmt @@ -146,11 +150,11 @@ @synchronizedstmt - 18562 + 18706 @returnstmt - 675212 + 675118 @throwstmt @@ -182,7 +186,7 @@ @localtypedeclstmt - 4069 + 4110 @constructorinvocationstmt @@ -190,7 +194,7 @@ @superconstructorinvocationstmt - 225902 + 226569 @case @@ -202,7 +206,7 @@ @labeledstmt - 2443 + 2518 @yieldstmt @@ -214,7 +218,7 @@ @whenbranch - 238370 + 233347 @arrayaccess @@ -334,7 +338,7 @@ @andlogicalexpr - 41441 + 41715 @orlogicalexpr @@ -410,7 +414,7 @@ @instanceofexpr - 31274 + 31086 @localvariabledeclexpr @@ -418,11 +422,11 @@ @typeliteral - 147176 + 148317 @thisaccess - 952512 + 952380 @superaccess @@ -434,11 +438,11 @@ @methodaccess - 1551591 + 1551738 @unannotatedtypeaccess - 2874591 + 2867985 @arraytypeaccess @@ -486,7 +490,7 @@ @lambdaexpr - 185816 + 186985 @memberref @@ -514,7 +518,7 @@ @whenexpr - 172153 + 172129 @getclassexpr @@ -522,35 +526,35 @@ @safecastexpr - 6986 + 6943 @implicitcastexpr - 33111 + 32911 @implicitnotnullexpr - 241008 + 239473 @implicitcoerciontounitexpr - 93086 + 92272 @notinstanceofexpr - 19586 + 19583 @stmtexpr - 58107 + 57757 @stringtemplateexpr - 55964 + 55943 @notnullexpr - 21375 + 21370 @unsafecoerceexpr @@ -558,15 +562,15 @@ @valueeqexpr - 103390 + 104218 @valueneexpr - 108240 + 108225 @propertyref - 9721 + 9663 @localvar @@ -610,7 +614,7 @@ @xmlelement - 106792352 + 106753032 @javadocText @@ -618,19 +622,19 @@ @xmlattribute - 129898822 + 129850995 @xmlnamespace - 8189 + 8186 @xmlcomment - 107485764 + 107446189 @xmlcharacters - 101574013 + 101536615 @config @@ -646,15 +650,15 @@ @ktcomment - 133768 + 133719 @ktcommentsection - 59246 + 59191 @kt_property - 30317687 + 30306525 @@ -876,30 +880,30 @@ compilation_started - 6824 + 6822 id - 6824 + 6822 compilation_args - 158338 + 158279 id - 6824 + 6822 num - 38219 + 38205 arg - 90089 + 90055 @@ -913,7 +917,7 @@ 20 21 - 2729 + 2728 23 @@ -944,7 +948,7 @@ 20 21 - 2729 + 2728 23 @@ -975,22 +979,22 @@ 1 2 - 4094 + 4093 2 3 - 2729 + 2728 3 4 - 4094 + 4093 5 6 - 27299 + 27289 @@ -1006,27 +1010,27 @@ 1 2 - 8189 + 8186 2 3 - 5459 + 5457 3 4 - 10919 + 10915 4 5 - 6824 + 6822 5 6 - 6824 + 6822 @@ -1042,22 +1046,22 @@ 1 2 - 69614 + 69588 2 3 - 2729 + 2728 4 5 - 6824 + 6822 5 6 - 10919 + 10915 @@ -1073,17 +1077,17 @@ 1 2 - 72344 + 72317 2 3 - 15014 + 15009 4 5 - 2729 + 2728 @@ -1093,19 +1097,19 @@ compilation_compiling_files - 60660 + 61130 id - 2320 + 2338 num - 17899 + 18038 file - 50716 + 51109 @@ -1119,22 +1123,22 @@ 1 2 - 331 + 334 2 3 - 662 + 668 35 36 - 662 + 668 54 55 - 662 + 668 @@ -1150,22 +1154,22 @@ 1 2 - 331 + 334 2 3 - 662 + 668 35 36 - 662 + 668 54 55 - 662 + 668 @@ -1181,17 +1185,17 @@ 2 3 - 6298 + 6346 4 5 - 10938 + 11023 6 8 - 662 + 668 @@ -1207,17 +1211,17 @@ 2 3 - 6298 + 6346 3 4 - 9944 + 10021 4 8 - 1657 + 1670 @@ -1233,12 +1237,12 @@ 1 2 - 40771 + 41087 2 3 - 9944 + 10021 @@ -1254,7 +1258,7 @@ 1 2 - 50716 + 51109 @@ -1264,19 +1268,19 @@ compilation_compiling_files_completed - 60660 + 61130 id - 2320 + 2338 num - 17899 + 18038 result - 331 + 668 @@ -1290,22 +1294,22 @@ 1 2 - 331 + 334 2 3 - 662 + 668 35 36 - 662 + 668 54 55 - 662 + 668 @@ -1321,7 +1325,12 @@ 1 2 - 2320 + 1670 + + + 2 + 3 + 668 @@ -1337,17 +1346,17 @@ 2 3 - 6298 + 6346 4 5 - 10938 + 11023 6 8 - 662 + 668 @@ -1363,7 +1372,12 @@ 1 2 - 17899 + 17704 + + + 2 + 3 + 334 @@ -1376,10 +1390,15 @@ 12 + + 2 + 3 + 334 + 7 8 - 331 + 334 @@ -1392,10 +1411,15 @@ 12 + + 1 + 2 + 334 + 54 55 - 331 + 334 @@ -1826,23 +1850,23 @@ diagnostic_for - 634718 + 624933 diagnostic - 634718 + 624933 compilation - 6824 + 6822 file_number - 8189 + 8186 file_number_diagnostic_number - 60059 + 60037 @@ -1856,7 +1880,7 @@ 1 2 - 634718 + 624933 @@ -1872,7 +1896,7 @@ 1 2 - 634718 + 624933 @@ -1888,7 +1912,7 @@ 1 2 - 634718 + 624933 @@ -1907,18 +1931,18 @@ 1364 - 86 - 87 + 84 + 85 1364 100 101 - 2729 + 2728 - 106 - 107 + 101 + 102 1364 @@ -1935,7 +1959,7 @@ 3 4 - 2729 + 2728 4 @@ -1969,8 +1993,8 @@ 1364 - 36 - 37 + 34 + 35 1364 @@ -1979,8 +2003,8 @@ 1364 - 43 - 44 + 41 + 42 1364 @@ -2005,23 +2029,23 @@ 1364 - 32 - 33 + 34 + 35 1364 - 49 - 50 + 48 + 49 1364 - 106 - 107 + 100 + 101 1364 - 130 - 131 + 128 + 129 1364 @@ -2058,7 +2082,7 @@ 5 6 - 4094 + 4093 @@ -2082,8 +2106,13 @@ 1364 - 36 - 37 + 34 + 35 + 1364 + + + 40 + 41 1364 @@ -2091,11 +2120,6 @@ 42 1364 - - 43 - 44 - 1364 - 44 45 @@ -2114,63 +2138,63 @@ 1 + 3 + 5457 + + + 3 4 - 5459 + 1364 4 5 - 5459 + 5457 5 - 7 - 2729 + 6 + 1364 7 8 - 6824 + 6822 8 9 - 4094 + 4093 9 10 - 6824 + 9551 10 - 12 - 4094 - - - 12 14 - 4094 + 5457 15 16 - 2729 + 4093 16 17 - 9554 + 6822 - 17 + 18 20 - 4094 + 5457 20 22 - 4094 + 4093 @@ -2186,22 +2210,22 @@ 1 3 - 5459 + 5457 3 4 - 5459 + 8186 4 5 - 9554 + 6822 5 6 - 39584 + 39569 @@ -2217,27 +2241,27 @@ 1 3 - 4094 + 5457 3 4 - 6824 + 8186 4 5 - 28664 + 25925 5 6 - 13649 + 13644 6 7 - 6824 + 6822 @@ -2247,11 +2271,11 @@ compilation_compiler_times - 6824 + 6822 id - 6824 + 6822 cpu_seconds @@ -2259,7 +2283,7 @@ elapsed_seconds - 6824 + 6822 @@ -2273,7 +2297,7 @@ 1 2 - 6824 + 6822 @@ -2289,7 +2313,7 @@ 1 2 - 6824 + 6822 @@ -2337,7 +2361,7 @@ 1 2 - 6824 + 6822 @@ -2353,7 +2377,7 @@ 1 2 - 6824 + 6822 @@ -2579,11 +2603,11 @@ diagnostics - 634718 + 624933 id - 634718 + 624933 generated_by @@ -2599,11 +2623,11 @@ error_message - 96913 + 95513 full_error_message - 499584 + 502129 location @@ -2621,7 +2645,7 @@ 1 2 - 634718 + 624933 @@ -2637,7 +2661,7 @@ 1 2 - 634718 + 624933 @@ -2653,7 +2677,7 @@ 1 2 - 634718 + 624933 @@ -2669,7 +2693,7 @@ 1 2 - 634718 + 624933 @@ -2685,7 +2709,7 @@ 1 2 - 634718 + 624933 @@ -2701,7 +2725,7 @@ 1 2 - 634718 + 624933 @@ -2715,8 +2739,8 @@ 12 - 465 - 466 + 458 + 459 1364 @@ -2763,8 +2787,8 @@ 12 - 71 - 72 + 70 + 71 1364 @@ -2779,8 +2803,8 @@ 12 - 366 - 367 + 368 + 369 1364 @@ -2811,8 +2835,8 @@ 12 - 465 - 466 + 458 + 459 1364 @@ -2859,8 +2883,8 @@ 12 - 71 - 72 + 70 + 71 1364 @@ -2875,8 +2899,8 @@ 12 - 366 - 367 + 368 + 369 1364 @@ -2907,8 +2931,8 @@ 12 - 465 - 466 + 458 + 459 1364 @@ -2955,8 +2979,8 @@ 12 - 71 - 72 + 70 + 71 1364 @@ -2971,8 +2995,8 @@ 12 - 366 - 367 + 368 + 369 1364 @@ -3005,67 +3029,62 @@ 1 2 - 19109 + 19102 2 3 - 6824 + 5457 3 4 - 13649 + 12280 4 5 - 4094 + 5457 5 6 - 5459 + 5457 6 7 - 6824 + 6822 7 8 - 2729 + 2728 8 9 - 9554 + 9551 9 10 - 5459 + 6822 10 11 - 6824 + 8186 - 13 - 15 - 5459 + 14 + 17 + 6822 - 16 - 19 - 8189 - - - 20 + 17 21 - 2729 + 6822 @@ -3081,7 +3100,7 @@ 1 2 - 96913 + 95513 @@ -3097,7 +3116,7 @@ 1 2 - 96913 + 95513 @@ -3113,7 +3132,7 @@ 1 2 - 96913 + 95513 @@ -3129,52 +3148,62 @@ 1 2 - 23204 + 21831 2 3 - 5459 + 5457 3 4 - 13649 + 12280 4 + 5 + 5457 + + + 5 6 - 8189 + 4093 6 7 - 6824 + 8186 7 8 - 6824 + 12280 8 9 - 16379 + 6822 9 10 - 2729 + 4093 10 11 - 6824 + 5457 11 12 - 6824 + 5457 + + + 12 + 13 + 4093 @@ -3190,7 +3219,7 @@ 1 2 - 96913 + 95513 @@ -3206,17 +3235,17 @@ 1 2 - 409495 + 418896 2 3 - 49139 + 49121 3 5 - 40949 + 34112 @@ -3232,7 +3261,7 @@ 1 2 - 499584 + 502129 @@ -3248,7 +3277,7 @@ 1 2 - 499584 + 502129 @@ -3264,7 +3293,7 @@ 1 2 - 499584 + 502129 @@ -3280,7 +3309,7 @@ 1 2 - 499584 + 502129 @@ -3296,7 +3325,7 @@ 1 2 - 499584 + 502129 @@ -3310,8 +3339,8 @@ 12 - 465 - 466 + 458 + 459 1364 @@ -3374,8 +3403,8 @@ 12 - 71 - 72 + 70 + 71 1364 @@ -3390,8 +3419,8 @@ 12 - 366 - 367 + 368 + 369 1364 @@ -4848,31 +4877,31 @@ locations_default - 431212495 + 431053728 id - 431212495 + 431053728 file - 8020653 + 8017700 beginLine - 2802314 + 2801282 beginColumn - 176083 + 176018 endLine - 2803679 + 2802647 endColumn - 621068 + 620839 @@ -4886,7 +4915,7 @@ 1 2 - 431212495 + 431053728 @@ -4902,7 +4931,7 @@ 1 2 - 431212495 + 431053728 @@ -4918,7 +4947,7 @@ 1 2 - 431212495 + 431053728 @@ -4934,7 +4963,7 @@ 1 2 - 431212495 + 431053728 @@ -4950,7 +4979,7 @@ 1 2 - 431212495 + 431053728 @@ -4966,17 +4995,17 @@ 1 2 - 7179822 + 7177178 2 20 - 604688 + 604465 20 3605 - 236142 + 236055 @@ -4992,17 +5021,17 @@ 1 2 - 7179822 + 7177178 2 15 - 601958 + 601736 15 1830 - 238872 + 238784 @@ -5018,17 +5047,17 @@ 1 2 - 7179822 + 7177178 2 7 - 629258 + 629026 7 105 - 211572 + 211494 @@ -5044,17 +5073,17 @@ 1 2 - 7179822 + 7177178 2 18 - 601958 + 601736 18 1834 - 238872 + 238784 @@ -5070,17 +5099,17 @@ 1 2 - 7179822 + 7177178 2 15 - 604688 + 604465 15 205 - 236142 + 236055 @@ -5096,67 +5125,67 @@ 1 14 - 222492 + 222410 14 125 - 215667 + 215588 125 142 - 215667 + 215588 142 152 - 223857 + 223775 152 159 - 249792 + 249700 159 165 - 257982 + 257887 165 170 - 226587 + 226504 170 174 - 217032 + 216952 174 179 - 229317 + 229233 179 185 - 214302 + 214223 185 194 - 222492 + 222410 194 215 - 215667 + 215588 215 5877 - 91454 + 91420 @@ -5172,67 +5201,67 @@ 1 7 - 229317 + 229233 7 65 - 212937 + 212859 65 73 - 217032 + 216952 73 78 - 207477 + 207401 78 81 - 191097 + 191027 81 84 - 249792 + 249700 84 86 - 215667 + 215588 86 88 - 257982 + 257887 88 90 - 222492 + 222410 90 93 - 248427 + 248335 93 97 - 218397 + 218317 97 106 - 214302 + 214223 106 5877 - 117388 + 117345 @@ -5248,62 +5277,62 @@ 1 5 - 222492 + 222410 5 17 - 196557 + 196485 17 19 - 167893 + 167831 19 20 - 214302 + 214223 20 21 - 268902 + 268803 21 22 - 283916 + 283812 22 23 - 330326 + 330204 23 24 - 304391 + 304279 24 25 - 236142 + 236055 25 26 - 170623 + 170560 26 28 - 212937 + 212859 28 45 - 193827 + 193756 @@ -5319,32 +5348,32 @@ 1 2 - 904985 + 904652 2 3 - 896795 + 896465 3 4 - 483204 + 483026 4 5 - 211572 + 211494 5 11 - 221127 + 221046 11 97 - 84629 + 84597 @@ -5360,72 +5389,72 @@ 1 13 - 219762 + 219681 13 60 - 210207 + 210130 60 64 - 223857 + 223775 64 66 - 210207 + 210130 66 68 - 259347 + 259251 68 69 - 131038 + 130990 69 70 - 155608 + 155551 70 71 - 135133 + 135083 71 73 - 236142 + 236055 73 75 - 212937 + 212859 75 78 - 234777 + 234691 78 82 - 252522 + 252429 82 89 - 214302 + 214223 89 104 - 106468 + 106429 @@ -5441,67 +5470,67 @@ 1 11 - 15014 + 15009 15 34 - 15014 + 15009 36 58 - 13649 + 13644 63 88 - 13649 + 13644 89 132 - 13649 + 13644 141 196 - 15014 + 15009 210 285 - 13649 + 13644 316 468 - 13649 + 13644 496 853 - 13649 + 13644 899 1420 - 13649 + 13644 1472 2256 - 13649 + 13644 2300 2526 - 13649 + 13644 2589 226687 - 8189 + 8186 @@ -5517,72 +5546,72 @@ 1 9 - 9554 + 9551 9 11 - 13649 + 13644 12 16 - 8189 + 8186 16 19 - 13649 + 13644 19 31 - 13649 + 13644 35 73 - 13649 + 13644 73 83 - 13649 + 13644 85 104 - 15014 + 15009 104 110 - 10919 + 10915 110 114 - 15014 + 15009 115 119 - 13649 + 13644 119 121 - 12284 + 12280 121 126 - 13649 + 13644 126 5877 - 9554 + 9551 @@ -5598,67 +5627,67 @@ 1 10 - 15014 + 15009 10 29 - 13649 + 13644 29 43 - 13649 + 13644 45 58 - 13649 + 13644 58 85 - 13649 + 13644 86 106 - 13649 + 13644 108 167 - 13649 + 13644 171 227 - 13649 + 13644 231 379 - 13649 + 13644 383 650 - 13649 + 13644 651 891 - 13649 + 13644 940 1086 - 13649 + 13644 1093 2051 - 10919 + 10915 @@ -5674,67 +5703,67 @@ 1 10 - 15014 + 15009 10 30 - 13649 + 13644 30 43 - 13649 + 13644 46 59 - 13649 + 13644 60 87 - 13649 + 13644 87 109 - 13649 + 13644 115 173 - 13649 + 13644 174 226 - 13649 + 13644 230 380 - 13649 + 13644 383 650 - 13649 + 13644 653 892 - 13649 + 13644 940 1084 - 13649 + 13644 1092 2051 - 10919 + 10915 @@ -5750,67 +5779,67 @@ 1 8 - 15014 + 15009 8 17 - 13649 + 13644 18 25 - 10919 + 10915 25 30 - 13649 + 13644 30 36 - 13649 + 13644 36 44 - 13649 + 13644 45 56 - 12284 + 12280 57 64 - 13649 + 13644 64 73 - 13649 + 13644 73 86 - 13649 + 13644 86 106 - 13649 + 13644 107 129 - 13649 + 13644 129 197 - 13649 + 13644 392 @@ -5831,67 +5860,67 @@ 1 14 - 221127 + 221046 14 124 - 215667 + 215588 124 143 - 218397 + 218317 143 152 - 236142 + 236055 152 159 - 223857 + 223775 159 165 - 257982 + 257887 165 170 - 240237 + 240148 170 174 - 222492 + 222410 174 179 - 222492 + 222410 179 185 - 211572 + 211494 185 194 - 217032 + 216952 194 217 - 212937 + 212859 217 5877 - 103738 + 103700 @@ -5907,67 +5936,67 @@ 1 7 - 232047 + 231962 7 66 - 225222 + 225139 66 74 - 227952 + 227868 74 80 - 251157 + 251064 80 83 - 241602 + 241513 83 85 - 185637 + 185569 85 87 - 247062 + 246971 87 89 - 247062 + 246971 89 91 - 188367 + 188298 91 94 - 248427 + 248335 94 99 - 226587 + 226504 99 127 - 212937 + 212859 127 5877 - 69614 + 69588 @@ -5983,32 +6012,32 @@ 1 2 - 711157 + 710895 2 3 - 989614 + 989249 3 4 - 644273 + 644035 4 6 - 237507 + 237419 6 18 - 212937 + 212859 18 22 - 8189 + 8186 @@ -6024,62 +6053,62 @@ 1 5 - 219762 + 219681 5 17 - 199287 + 199214 17 19 - 163798 + 163737 19 20 - 192462 + 192392 20 21 - 281186 + 281083 21 22 - 272997 + 272896 22 23 - 330326 + 330204 23 24 - 307121 + 307008 24 25 - 225222 + 225139 25 26 - 184273 + 184205 26 28 - 223857 + 223775 28 44 - 203382 + 203307 @@ -6095,72 +6124,72 @@ 1 13 - 222492 + 222410 13 61 - 251157 + 251064 61 64 - 173353 + 173289 64 66 - 218397 + 218317 66 68 - 251157 + 251064 68 69 - 137863 + 137812 69 70 - 137863 + 137812 70 71 - 158338 + 158279 71 73 - 232047 + 231962 73 75 - 192462 + 192392 75 77 - 184273 + 184205 77 80 - 211572 + 211494 80 85 - 227952 + 227868 85 119 - 204747 + 204672 @@ -6176,57 +6205,57 @@ 1 2 - 146053 + 145999 2 3 - 64154 + 64130 3 5 - 50504 + 50485 5 13 - 50504 + 50485 13 53 - 47774 + 47756 53 146 - 47774 + 47756 146 351 - 47774 + 47756 357 997 - 47774 + 47756 1053 2396 - 47774 + 47756 2407 4957 - 47774 + 47756 5022 5934 - 23204 + 23196 @@ -6242,57 +6271,57 @@ 1 2 - 151513 + 151457 2 3 - 61424 + 61401 3 5 - 53234 + 53214 5 13 - 50504 + 50485 13 42 - 47774 + 47756 42 77 - 47774 + 47756 77 103 - 51869 + 51850 103 116 - 47774 + 47756 116 144 - 49139 + 49121 144 181 - 47774 + 47756 181 5877 - 12284 + 12280 @@ -6308,57 +6337,57 @@ 1 2 - 154243 + 154186 2 3 - 62789 + 62766 3 5 - 49139 + 49121 5 13 - 49139 + 49121 13 52 - 49139 + 49121 52 115 - 47774 + 47756 123 271 - 47774 + 47756 277 658 - 47774 + 47756 669 1200 - 47774 + 47756 1219 1635 - 47774 + 47756 1639 1722 - 17744 + 17738 @@ -6374,47 +6403,47 @@ 1 2 - 195192 + 195121 2 3 - 75074 + 75046 3 6 - 54599 + 54579 6 14 - 54599 + 54579 14 26 - 47774 + 47756 26 36 - 49139 + 49121 36 47 - 49139 + 49121 47 55 - 47774 + 47756 55 68 - 47774 + 47756 @@ -6430,57 +6459,57 @@ 1 2 - 154243 + 154186 2 3 - 61424 + 61401 3 5 - 49139 + 49121 5 13 - 49139 + 49121 13 53 - 50504 + 50485 53 115 - 47774 + 47756 123 271 - 47774 + 47756 280 656 - 47774 + 47756 669 1200 - 47774 + 47756 1217 1638 - 47774 + 47756 1640 1722 - 17744 + 17738 @@ -6490,15 +6519,15 @@ hasLocation - 316110113 + 316902470 locatableid - 315965424 + 316759199 id - 11558695 + 11554439 @@ -6512,12 +6541,12 @@ 1 2 - 315820736 + 316615929 2 3 - 144688 + 143270 @@ -6533,62 +6562,62 @@ 1 2 - 2097982 + 2097209 2 3 - 1008724 + 1008352 3 4 - 719347 + 717717 4 6 - 977329 + 978334 6 7 - 773946 + 773661 7 9 - 1019643 + 1019268 9 12 - 850385 + 848708 12 16 - 925460 + 926483 16 23 - 883145 + 882820 23 39 - 888605 + 888278 39 89 - 874955 + 873268 89 - 9987 - 539169 + 9991 + 540335 @@ -6598,23 +6627,23 @@ numlines - 215080728 + 215001538 element_id - 215080728 + 215001538 num_lines - 432700 + 432541 num_code - 434065 + 433905 num_comment - 1345875 + 1345379 @@ -6628,7 +6657,7 @@ 1 2 - 215080728 + 215001538 @@ -6644,7 +6673,7 @@ 1 2 - 215080728 + 215001538 @@ -6660,7 +6689,7 @@ 1 2 - 215080728 + 215001538 @@ -6676,37 +6705,37 @@ 1 2 - 232047 + 231962 2 3 - 53234 + 53214 3 4 - 45044 + 45027 4 7 - 34124 + 34112 7 14 - 32759 + 32747 15 839 - 32759 + 32747 3519 149603 - 2729 + 2728 @@ -6722,12 +6751,12 @@ 1 2 - 423145 + 422989 2 3 - 9554 + 9551 @@ -6743,27 +6772,27 @@ 1 2 - 274362 + 274261 2 3 - 69614 + 69588 3 4 - 36854 + 36841 4 6 - 34124 + 34112 6 987 - 17744 + 17738 @@ -6779,37 +6808,37 @@ 1 2 - 232047 + 231962 2 3 - 53234 + 53214 3 4 - 45044 + 45027 4 7 - 34124 + 34112 7 14 - 32759 + 32747 15 468 - 32759 + 32747 495 78746 - 4094 + 4093 @@ -6825,7 +6854,7 @@ 1 2 - 432700 + 432541 7 @@ -6846,27 +6875,27 @@ 1 2 - 274362 + 274261 2 3 - 69614 + 69588 3 4 - 36854 + 36841 4 6 - 34124 + 34112 6 987 - 19109 + 19102 @@ -6882,77 +6911,77 @@ 1 7 - 109198 + 109158 7 49 - 101008 + 100971 49 71 - 105103 + 105065 71 78 - 107833 + 107794 78 83 - 103738 + 103700 83 87 - 116023 + 115981 87 89 - 99643 + 99607 89 91 - 95548 + 95513 91 92 - 57329 + 57308 92 93 - 68249 + 68224 93 94 - 75074 + 75046 94 95 - 69614 + 69588 95 97 - 109198 + 109158 97 119 - 101008 + 100971 119 75115 - 27299 + 27289 @@ -6968,22 +6997,22 @@ 1 2 - 1104273 + 1103866 2 3 - 114658 + 114616 3 6 - 102373 + 102336 6 120 - 24569 + 24560 @@ -6999,22 +7028,22 @@ 1 2 - 1104273 + 1103866 2 3 - 114658 + 114616 3 6 - 101008 + 100971 6 121 - 25934 + 25925 @@ -7024,15 +7053,15 @@ files - 8020653 + 8017700 id - 8020653 + 8017700 name - 8020653 + 8017700 @@ -7046,7 +7075,7 @@ 1 2 - 8020653 + 8017700 @@ -7062,7 +7091,7 @@ 1 2 - 8020653 + 8017700 @@ -7072,15 +7101,15 @@ folders - 1280356 + 1279884 id - 1280356 + 1279884 name - 1280356 + 1279884 @@ -7094,7 +7123,7 @@ 1 2 - 1280356 + 1279884 @@ -7110,7 +7139,7 @@ 1 2 - 1280356 + 1279884 @@ -7120,15 +7149,15 @@ containerparent - 9298279 + 9294856 parent - 1321305 + 1320819 child - 9298279 + 9294856 @@ -7142,37 +7171,37 @@ 1 2 - 713887 + 713624 2 3 - 140593 + 140541 3 4 - 79169 + 79139 4 7 - 113293 + 113252 7 14 - 111928 + 111887 14 29 - 102373 + 102336 29 194 - 60059 + 60037 @@ -7188,7 +7217,7 @@ 1 2 - 9298279 + 9294856 @@ -7198,15 +7227,15 @@ cupackage - 7160712 + 7158076 id - 7160712 + 7158076 packageid - 611513 + 611288 @@ -7220,7 +7249,7 @@ 1 2 - 7160712 + 7158076 @@ -7236,52 +7265,52 @@ 1 2 - 148783 + 148728 2 3 - 80534 + 80504 3 4 - 55964 + 55943 4 5 - 42314 + 42298 5 7 - 46409 + 46392 7 10 - 50504 + 50485 10 15 - 50504 + 50485 15 21 - 49139 + 49121 21 36 - 47774 + 47756 39 187 - 39584 + 39569 @@ -8043,15 +8072,15 @@ packages - 612878 + 612652 id - 612878 + 612652 nodeName - 612878 + 612652 @@ -8065,7 +8094,7 @@ 1 2 - 612878 + 612652 @@ -8081,7 +8110,7 @@ 1 2 - 612878 + 612652 @@ -8091,15 +8120,15 @@ primitives - 12284 + 12280 id - 12284 + 12280 nodeName - 12284 + 12280 @@ -8113,7 +8142,7 @@ 1 2 - 12284 + 12280 @@ -8129,7 +8158,7 @@ 1 2 - 12284 + 12280 @@ -8139,15 +8168,15 @@ modifiers - 12284 + 13644 id - 12284 + 13644 nodeName - 12284 + 13644 @@ -8161,7 +8190,7 @@ 1 2 - 12284 + 13644 @@ -8177,7 +8206,7 @@ 1 2 - 12284 + 13644 @@ -8186,24 +8215,35 @@ - classes - 12579704 + error_type + 1 id - 12579704 + 1 + + + + + + classes + 12579165 + + + id + 12579165 nodeName - 6868605 + 6871534 parentid - 443620 + 443456 sourceid - 4519466 + 4517802 @@ -8217,7 +8257,7 @@ 1 2 - 12579704 + 12579165 @@ -8233,7 +8273,7 @@ 1 2 - 12579704 + 12579165 @@ -8249,7 +8289,7 @@ 1 2 - 12579704 + 12579165 @@ -8265,17 +8305,17 @@ 1 2 - 5735668 + 5740378 2 3 - 746646 + 745007 3 236 - 386290 + 386148 @@ -8291,17 +8331,17 @@ 1 2 - 6252997 + 6256153 2 3 - 547359 + 547157 3 52 - 68249 + 68224 @@ -8317,17 +8357,17 @@ 1 2 - 6229792 + 6232956 2 3 - 536439 + 536241 3 160 - 102373 + 102336 @@ -8343,57 +8383,57 @@ 1 2 - 107833 + 107794 2 3 - 54599 + 54579 3 4 - 32759 + 32747 4 5 - 30029 + 30018 5 7 - 34124 + 34112 7 11 - 40949 + 40934 11 17 - 34124 + 34112 17 23 - 34124 + 34112 23 40 - 36854 + 36841 40 707 - 34124 + 34112 1013 - 1412 - 4094 + 1414 + 4093 @@ -8409,52 +8449,52 @@ 1 2 - 107833 + 107794 2 3 - 54599 + 54579 3 4 - 35489 + 35476 4 5 - 34124 + 34112 5 7 - 38219 + 38205 7 11 - 40949 + 40934 11 17 - 36854 + 36841 17 23 - 34124 + 34112 23 40 - 35489 + 35476 40 - 828 - 25934 + 830 + 25925 @@ -8470,52 +8510,52 @@ 1 2 - 118753 + 118709 2 3 - 64154 + 64130 3 4 - 36854 + 36841 4 5 - 34124 + 34112 5 7 - 35489 + 35476 7 11 - 40949 + 40934 11 17 - 34124 + 34112 17 26 - 34124 + 34112 26 56 - 34124 + 34112 64 138 - 10919 + 10915 @@ -8531,17 +8571,17 @@ 1 2 - 4052641 + 4051149 2 11 - 342611 + 342485 11 1358 - 124213 + 124167 @@ -8557,17 +8597,17 @@ 1 2 - 4052641 + 4051149 2 6 - 360356 + 360223 6 783 - 106468 + 106429 @@ -8583,7 +8623,7 @@ 1 2 - 4519466 + 4517802 @@ -8593,26 +8633,26 @@ file_class - 15014 + 15009 id - 15014 + 15009 class_object - 122848 + 122803 id - 122848 + 122803 instance - 122848 + 122803 @@ -8626,7 +8666,7 @@ 1 2 - 122848 + 122803 @@ -8642,7 +8682,7 @@ 1 2 - 122848 + 122803 @@ -8652,19 +8692,19 @@ type_companion_object - 218397 + 218317 id - 218397 + 218317 instance - 218397 + 218317 companion_object - 218397 + 218317 @@ -8678,7 +8718,7 @@ 1 2 - 218397 + 218317 @@ -8694,7 +8734,7 @@ 1 2 - 218397 + 218317 @@ -8710,7 +8750,7 @@ 1 2 - 218397 + 218317 @@ -8726,7 +8766,7 @@ 1 2 - 218397 + 218317 @@ -8742,7 +8782,7 @@ 1 2 - 218397 + 218317 @@ -8758,7 +8798,7 @@ 1 2 - 218397 + 218317 @@ -8816,15 +8856,15 @@ kt_notnull_types - 193427 + 194340 id - 193427 + 194340 classid - 193427 + 194340 @@ -8838,7 +8878,7 @@ 1 2 - 193427 + 194340 @@ -8854,7 +8894,7 @@ 1 2 - 193427 + 194340 @@ -9437,15 +9477,15 @@ fielddecls - 399940 + 399793 id - 399940 + 399793 parentid - 60059 + 60037 @@ -9459,7 +9499,7 @@ 1 2 - 399940 + 399793 @@ -9475,32 +9515,32 @@ 1 2 - 30029 + 30018 2 3 - 13649 + 13644 3 4 - 5459 + 5457 4 5 - 2729 + 2728 6 16 - 5459 + 5457 40 159 - 2729 + 2728 @@ -9510,15 +9550,15 @@ fieldDeclaredIn - 399940 + 399793 fieldId - 399940 + 399793 fieldDeclId - 399940 + 399793 pos @@ -9536,7 +9576,7 @@ 1 2 - 399940 + 399793 @@ -9552,7 +9592,7 @@ 1 2 - 399940 + 399793 @@ -9568,7 +9608,7 @@ 1 2 - 399940 + 399793 @@ -9584,7 +9624,7 @@ 1 2 - 399940 + 399793 @@ -9626,27 +9666,27 @@ fields - 27583622 + 27573466 id - 27583622 + 27573466 nodeName - 10929437 + 10925412 typeid - 2735430 + 2734423 parentid - 3861543 + 3860121 sourceid - 27583622 + 27573466 @@ -9660,7 +9700,7 @@ 1 2 - 27583622 + 27573466 @@ -9676,7 +9716,7 @@ 1 2 - 27583622 + 27573466 @@ -9692,7 +9732,7 @@ 1 2 - 27583622 + 27573466 @@ -9708,7 +9748,7 @@ 1 2 - 27583622 + 27573466 @@ -9724,22 +9764,22 @@ 1 2 - 8083442 + 8080466 2 3 - 1437329 + 1436800 3 11 - 763026 + 762745 11 828 - 645638 + 645400 @@ -9755,17 +9795,17 @@ 1 2 - 9901603 + 9897957 2 4 - 853115 + 852801 4 160 - 174718 + 174653 @@ -9781,22 +9821,22 @@ 1 2 - 8083442 + 8080466 2 3 - 1437329 + 1436800 3 11 - 763026 + 762745 11 828 - 645638 + 645400 @@ -9812,22 +9852,22 @@ 1 2 - 8083442 + 8080466 2 3 - 1437329 + 1436800 3 11 - 763026 + 762745 11 828 - 645638 + 645400 @@ -9843,32 +9883,32 @@ 1 2 - 1737626 + 1736986 2 3 - 339881 + 339756 3 4 - 176083 + 176018 4 7 - 208842 + 208765 7 24 - 206112 + 206036 24 7479 - 66884 + 66859 @@ -9884,27 +9924,27 @@ 1 2 - 1900059 + 1899359 2 3 - 303026 + 302915 3 4 - 184273 + 184205 4 9 - 212937 + 212859 9 2335 - 135133 + 135083 @@ -9920,17 +9960,17 @@ 1 2 - 2332759 + 2331900 2 4 - 229317 + 229233 4 1392 - 173353 + 173289 @@ -9946,32 +9986,32 @@ 1 2 - 1737626 + 1736986 2 3 - 339881 + 339756 3 4 - 176083 + 176018 4 7 - 208842 + 208765 7 24 - 206112 + 206036 24 7479 - 66884 + 66859 @@ -9987,37 +10027,37 @@ 1 2 - 1942374 + 1941658 2 3 - 485934 + 485755 3 4 - 304391 + 304279 4 6 - 342611 + 342485 6 10 - 301661 + 301550 10 27 - 292106 + 291999 27 1610 - 192462 + 192392 @@ -10033,37 +10073,37 @@ 1 2 - 1942374 + 1941658 2 3 - 485934 + 485755 3 4 - 304391 + 304279 4 6 - 342611 + 342485 6 10 - 301661 + 301550 10 27 - 292106 + 291999 27 1610 - 192462 + 192392 @@ -10079,27 +10119,27 @@ 1 2 - 2506112 + 2505190 2 3 - 625163 + 624933 3 4 - 244332 + 244242 4 7 - 339881 + 339756 7 76 - 146053 + 145999 @@ -10115,37 +10155,37 @@ 1 2 - 1942374 + 1941658 2 3 - 485934 + 485755 3 4 - 304391 + 304279 4 6 - 342611 + 342485 6 10 - 301661 + 301550 10 27 - 292106 + 291999 27 1610 - 192462 + 192392 @@ -10161,7 +10201,7 @@ 1 2 - 27583622 + 27573466 @@ -10177,7 +10217,7 @@ 1 2 - 27583622 + 27573466 @@ -10193,7 +10233,7 @@ 1 2 - 27583622 + 27573466 @@ -10209,7 +10249,7 @@ 1 2 - 27583622 + 27573466 @@ -10219,11 +10259,11 @@ fieldsKotlinType - 27583622 + 27573466 id - 27583622 + 27573466 kttypeid @@ -10241,7 +10281,7 @@ 1 2 - 27583622 + 27573466 @@ -10267,31 +10307,31 @@ constrs - 6889080 + 6886544 id - 6889080 + 6886544 nodeName - 3756439 + 3755056 signature - 5887181 + 5885013 typeid - 2729 + 2728 parentid - 4849792 + 4848007 sourceid - 5069555 + 5067688 @@ -10305,7 +10345,7 @@ 1 2 - 6889080 + 6886544 @@ -10321,7 +10361,7 @@ 1 2 - 6889080 + 6886544 @@ -10337,7 +10377,7 @@ 1 2 - 6889080 + 6886544 @@ -10353,7 +10393,7 @@ 1 2 - 6889080 + 6886544 @@ -10369,7 +10409,7 @@ 1 2 - 6889080 + 6886544 @@ -10385,22 +10425,22 @@ 1 2 - 2366884 + 2366012 2 3 - 839465 + 839156 3 5 - 346706 + 346578 5 42 - 203382 + 203307 @@ -10416,22 +10456,22 @@ 1 2 - 2635786 + 2634816 2 3 - 685222 + 684970 3 5 - 303026 + 302915 5 19 - 132403 + 132354 @@ -10447,7 +10487,7 @@ 1 2 - 3756439 + 3755056 @@ -10463,17 +10503,17 @@ 1 2 - 3295074 + 3293861 2 3 - 316676 + 316559 3 42 - 144688 + 144635 @@ -10489,22 +10529,22 @@ 1 2 - 2458338 + 2457433 2 3 - 831276 + 830969 3 5 - 319406 + 319288 5 38 - 147418 + 147364 @@ -10520,12 +10560,12 @@ 1 2 - 5474955 + 5472940 2 42 - 412225 + 412073 @@ -10541,7 +10581,7 @@ 1 2 - 5887181 + 5885013 @@ -10557,7 +10597,7 @@ 1 2 - 5887181 + 5885013 @@ -10573,12 +10613,12 @@ 1 2 - 5474955 + 5472940 2 42 - 412225 + 412073 @@ -10594,12 +10634,12 @@ 1 2 - 5604629 + 5602565 2 32 - 282551 + 282447 @@ -10720,22 +10760,22 @@ 1 2 - 3688190 + 3686832 2 3 - 739822 + 739549 3 6 - 367181 + 367045 6 19 - 54599 + 54579 @@ -10751,7 +10791,7 @@ 1 2 - 4849792 + 4848007 @@ -10767,22 +10807,22 @@ 1 2 - 3688190 + 3686832 2 3 - 739822 + 739549 3 6 - 367181 + 367045 6 19 - 54599 + 54579 @@ -10798,7 +10838,7 @@ 1 2 - 4849792 + 4848007 @@ -10814,22 +10854,22 @@ 1 2 - 3688190 + 3686832 2 3 - 739822 + 739549 3 6 - 367181 + 367045 6 19 - 54599 + 54579 @@ -10845,12 +10885,12 @@ 1 2 - 4756973 + 4755222 2 259 - 312581 + 312466 @@ -10866,12 +10906,12 @@ 1 2 - 4756973 + 4755222 2 212 - 312581 + 312466 @@ -10887,12 +10927,12 @@ 1 2 - 4756973 + 4755222 2 212 - 312581 + 312466 @@ -10908,7 +10948,7 @@ 1 2 - 5069555 + 5067688 @@ -10924,12 +10964,12 @@ 1 2 - 4756973 + 4755222 2 259 - 312581 + 312466 @@ -10939,11 +10979,11 @@ constrsKotlinType - 6889080 + 6886544 id - 6889080 + 6886544 kttypeid @@ -10961,7 +11001,7 @@ 1 2 - 6889080 + 6886544 @@ -10987,31 +11027,31 @@ methods - 93482380 + 93447961 id - 93482380 + 93447961 nodeName - 20395609 + 20385371 signature - 29871337 + 29857610 typeid - 11611929 + 11610383 parentid - 11303442 + 11299281 sourceid - 58623387 + 58601802 @@ -11025,7 +11065,7 @@ 1 2 - 93482380 + 93447961 @@ -11041,7 +11081,7 @@ 1 2 - 93482380 + 93447961 @@ -11057,7 +11097,7 @@ 1 2 - 93482380 + 93447961 @@ -11073,7 +11113,7 @@ 1 2 - 93482380 + 93447961 @@ -11089,7 +11129,7 @@ 1 2 - 93482380 + 93447961 @@ -11105,32 +11145,32 @@ 1 2 - 11927241 + 11921485 2 3 - 3791929 + 3790532 3 4 - 1317210 + 1316725 4 7 - 1789495 + 1788836 7 - 259 - 1530148 + 271 + 1529585 - 270 + 276 2134 - 39584 + 38205 @@ -11146,17 +11186,17 @@ 1 2 - 16912167 + 16903211 2 3 - 2122552 + 2121770 3 361 - 1360890 + 1360389 @@ -11172,22 +11212,22 @@ 1 2 - 17141484 + 17133809 2 3 - 1683026 + 1682407 3 - 52 - 1530148 + 53 + 1529585 - 52 + 53 845 - 40949 + 39569 @@ -11203,27 +11243,27 @@ 1 2 - 12654778 + 12648754 2 3 - 3558516 + 3557206 3 4 - 1250326 + 1249866 4 7 - 1575192 + 1574613 7 1278 - 1356795 + 1354931 @@ -11239,27 +11279,27 @@ 1 2 - 12309437 + 12302175 2 3 - 3921602 + 3921523 3 4 - 1298100 + 1297623 4 7 - 1654362 + 1653753 7 928 - 1212106 + 1210296 @@ -11275,22 +11315,22 @@ 1 2 - 20522553 + 20513632 2 3 - 4879822 + 4878025 3 5 - 2368249 + 2367377 5 1275 - 2100712 + 2098574 @@ -11306,7 +11346,7 @@ 1 2 - 29871337 + 29857610 @@ -11322,17 +11362,17 @@ 1 2 - 26823325 + 26812084 2 5 - 2390089 + 2389209 5 843 - 657922 + 656316 @@ -11348,22 +11388,22 @@ 1 2 - 20526648 + 20517726 2 3 - 4878457 + 4876661 3 5 - 2366884 + 2366012 5 1275 - 2099347 + 2097209 @@ -11379,22 +11419,22 @@ 1 2 - 21147716 + 21137201 2 3 - 4997211 + 4996735 3 6 - 2537507 + 2536573 6 923 - 1188902 + 1187099 @@ -11410,32 +11450,32 @@ 1 2 - 5705638 + 5703537 2 3 - 2455608 + 2457433 3 4 - 1087893 + 1087492 4 6 - 924095 + 925119 6 14 - 877685 + 875997 14 11682 - 561008 + 560802 @@ -11451,22 +11491,22 @@ 1 2 - 7632997 + 7632916 2 3 - 2222196 + 2221377 3 6 - 1063323 + 1064296 6 3956 - 693412 + 691792 @@ -11482,27 +11522,27 @@ 1 2 - 7390030 + 7388673 2 3 - 2276795 + 2278686 3 5 - 879050 + 878726 5 17 - 884510 + 882820 17 5707 - 181543 + 181476 @@ -11518,27 +11558,27 @@ 1 2 - 6811276 + 6808768 2 3 - 2467893 + 2471078 3 4 - 1004629 + 1004259 4 9 - 917270 + 915567 9 3421 - 410860 + 410709 @@ -11554,32 +11594,32 @@ 1 2 - 6202493 + 6200209 2 3 - 2268605 + 2270499 3 4 - 984154 + 983792 4 6 - 909080 + 910109 6 16 - 892700 + 891007 16 8870 - 354896 + 354765 @@ -11595,52 +11635,52 @@ 1 2 - 3200890 + 3199711 2 3 - 1298100 + 1297623 3 4 - 1027833 + 1027455 4 5 - 748011 + 747736 5 7 - 925460 + 925119 7 10 - 959584 + 959231 10 13 - 997804 + 997436 13 19 - 909080 + 908745 19 38 - 910445 + 910109 38 319 - 326231 + 326111 @@ -11656,52 +11696,52 @@ 1 2 - 3254124 + 3252926 2 3 - 1318575 + 1318090 3 4 - 1108368 + 1107959 4 5 - 788961 + 788670 5 7 - 888605 + 888278 7 10 - 1021008 + 1020633 10 13 - 941839 + 942857 13 17 - 876320 + 874633 17 35 - 850385 + 850072 35 290 - 255252 + 255158 @@ -11717,52 +11757,52 @@ 1 2 - 3200890 + 3199711 2 3 - 1298100 + 1297623 3 4 - 1027833 + 1027455 4 5 - 748011 + 747736 5 7 - 925460 + 925119 7 10 - 959584 + 959231 10 13 - 997804 + 997436 13 19 - 909080 + 908745 19 38 - 910445 + 910109 38 319 - 326231 + 326111 @@ -11778,47 +11818,47 @@ 1 2 - 3901127 + 3899691 2 3 - 1624332 + 1623734 3 4 - 1329495 + 1329006 4 5 - 790326 + 790035 5 6 - 636083 + 635848 6 7 - 502314 + 502129 7 9 - 1034658 + 1034277 9 13 - 881780 + 881455 13 78 - 603323 + 603101 @@ -11834,52 +11874,52 @@ 1 2 - 3200890 + 3199711 2 3 - 1298100 + 1297623 3 4 - 1027833 + 1027455 4 5 - 748011 + 747736 5 7 - 925460 + 925119 7 10 - 959584 + 959231 10 13 - 997804 + 997436 13 19 - 909080 + 908745 19 38 - 910445 + 910109 38 319 - 326231 + 326111 @@ -11895,12 +11935,12 @@ 1 2 - 54259529 + 54239551 2 349 - 4363857 + 4362251 @@ -11916,7 +11956,7 @@ 1 2 - 58623387 + 58601802 @@ -11932,12 +11972,12 @@ 1 2 - 58339470 + 58317990 2 347 - 283916 + 283812 @@ -11953,12 +11993,12 @@ 1 2 - 56923980 + 56903021 2 259 - 1699406 + 1698780 @@ -11974,12 +12014,12 @@ 1 2 - 54259529 + 54239551 2 349 - 4363857 + 4362251 @@ -11989,11 +12029,11 @@ methodsKotlinType - 93482380 + 93447961 id - 93482380 + 93447961 kttypeid @@ -12011,7 +12051,7 @@ 1 2 - 93482380 + 93447961 @@ -12037,27 +12077,27 @@ params - 101137218 + 101099980 id - 101137218 + 101099980 typeid - 11278873 + 11198309 pos - 30029 + 30018 parentid - 56339766 + 56319023 sourceid - 61326058 + 61303478 @@ -12071,7 +12111,7 @@ 1 2 - 101137218 + 101099980 @@ -12087,7 +12127,7 @@ 1 2 - 101137218 + 101099980 @@ -12103,7 +12143,7 @@ 1 2 - 101137218 + 101099980 @@ -12119,7 +12159,7 @@ 1 2 - 101137218 + 101099980 @@ -12135,32 +12175,32 @@ 1 2 - 6038694 + 5955966 2 3 - 1756736 + 1720612 3 4 - 853115 + 873268 4 6 - 966409 + 974240 6 12 - 854480 + 866446 12 7464 - 809436 + 807773 @@ -12176,17 +12216,17 @@ 1 2 - 9331039 + 9255286 2 3 - 1156142 + 1159810 3 17 - 791691 + 783213 @@ -12202,32 +12242,32 @@ 1 2 - 6227062 + 6146994 2 3 - 1726706 + 1685136 3 4 - 873590 + 893736 4 6 - 909080 + 918296 6 13 - 854480 + 866446 13 5239 - 687952 + 687699 @@ -12243,32 +12283,32 @@ 1 2 - 6433175 + 6350302 2 3 - 1613412 + 1577341 3 4 - 884510 + 904652 4 6 - 943204 + 951044 6 - 14 - 889970 + 13 + 854166 - 14 + 13 6287 - 514599 + 560802 @@ -12284,57 +12324,57 @@ 1 2 - 2729 + 2728 53 56 - 2729 + 2728 110 112 - 2729 + 2728 165 172 - 2729 + 2728 224 242 - 2729 + 2728 304 329 - 2729 + 2728 523 665 - 2729 + 2728 879 1140 - 2729 + 2728 1514 2087 - 2729 + 2728 3295 5932 - 2729 + 2728 15024 41276 - 2729 + 2728 @@ -12350,57 +12390,57 @@ 1 2 - 2729 + 2728 2 5 - 2729 + 2728 6 7 - 2729 + 2728 10 12 - 2729 + 2728 13 19 - 2729 + 2728 26 38 - 2729 + 2728 57 76 - 2729 + 2728 99 159 - 2729 + 2728 212 - 305 - 2729 + 306 + 2728 - 451 + 452 888 - 2729 + 2728 - 2378 - 6326 - 2729 + 2370 + 6266 + 2728 @@ -12416,57 +12456,57 @@ 1 2 - 2729 + 2728 53 56 - 2729 + 2728 110 112 - 2729 + 2728 165 172 - 2729 + 2728 224 242 - 2729 + 2728 304 329 - 2729 + 2728 523 665 - 2729 + 2728 879 1140 - 2729 + 2728 1514 2087 - 2729 + 2728 3295 5932 - 2729 + 2728 15024 41276 - 2729 + 2728 @@ -12482,57 +12522,57 @@ 1 2 - 2729 + 2728 2 5 - 2729 + 2728 6 8 - 2729 + 2728 10 13 - 2729 + 2728 14 28 - 2729 + 2728 37 62 - 2729 + 2728 97 137 - 2729 + 2728 192 342 - 2729 + 2728 553 963 - 2729 + 2728 1950 4155 - 2729 + 2728 10027 26335 - 2729 + 2728 @@ -12548,27 +12588,27 @@ 1 2 - 35832228 + 35819035 2 3 - 12411811 + 12407241 3 4 - 3598101 + 3596776 4 15 - 4264213 + 4262643 15 23 - 233412 + 233326 @@ -12584,17 +12624,17 @@ 1 2 - 39830270 + 39815605 2 3 - 12671158 + 12666492 3 23 - 3838338 + 3836925 @@ -12610,27 +12650,27 @@ 1 2 - 35832228 + 35819035 2 3 - 12411811 + 12407241 3 4 - 3598101 + 3596776 4 15 - 4264213 + 4262643 15 23 - 233412 + 233326 @@ -12646,27 +12686,27 @@ 1 2 - 35832228 + 35819035 2 3 - 12411811 + 12407241 3 4 - 3598101 + 3596776 4 15 - 4264213 + 4262643 15 23 - 233412 + 233326 @@ -12682,12 +12722,12 @@ 1 2 - 56895315 + 56874367 2 349 - 4430742 + 4429110 @@ -12703,12 +12743,12 @@ 1 2 - 59783624 + 59761613 2 349 - 1542433 + 1541865 @@ -12724,7 +12764,7 @@ 1 2 - 61326058 + 61303478 @@ -12740,12 +12780,12 @@ 1 2 - 56895315 + 56874367 2 349 - 4430742 + 4429110 @@ -12755,11 +12795,11 @@ paramsKotlinType - 101137218 + 101099980 id - 101137218 + 101099980 kttypeid @@ -12777,7 +12817,7 @@ 1 2 - 101137218 + 101099980 @@ -12803,15 +12843,15 @@ paramName - 101137218 + 10199508 id - 101137218 + 10199508 nodeName - 1688486 + 1666033 @@ -12825,7 +12865,7 @@ 1 2 - 101137218 + 10199508 @@ -12841,37 +12881,37 @@ 1 2 - 719347 + 729998 2 3 - 331691 + 338391 3 4 - 173353 + 169195 4 5 - 114658 + 117345 5 9 - 144688 + 140541 9 25 - 126943 + 126896 25 - 32680 - 77804 + 516 + 43663 @@ -12881,11 +12921,11 @@ isVarargsParam - 1005994 + 1005623 param - 1005994 + 1005623 @@ -13108,11 +13148,11 @@ isAnnotType - 29833 + 30064 interfaceid - 29833 + 30064 @@ -13396,11 +13436,11 @@ isEnumType - 350801 + 350672 classid - 350801 + 350672 @@ -13418,19 +13458,19 @@ typeVars - 5118694 + 5116810 id - 5118694 + 5116810 nodeName - 70979 + 70953 pos - 5459 + 5457 kind @@ -13438,7 +13478,7 @@ parentid - 3725044 + 3723673 @@ -13452,7 +13492,7 @@ 1 2 - 5118694 + 5116810 @@ -13468,7 +13508,7 @@ 1 2 - 5118694 + 5116810 @@ -13484,7 +13524,7 @@ 1 2 - 5118694 + 5116810 @@ -13500,7 +13540,7 @@ 1 2 - 5118694 + 5116810 @@ -13516,47 +13556,47 @@ 1 2 - 20474 + 20467 2 3 - 10919 + 10915 3 4 - 6824 + 6822 4 7 - 5459 + 5457 7 10 - 5459 + 5457 10 28 - 5459 + 5457 37 71 - 5459 + 5457 71 253 - 5459 + 5457 459 951 - 5459 + 5457 @@ -13572,17 +13612,17 @@ 1 2 - 45044 + 45027 2 3 - 16379 + 16373 3 4 - 8189 + 8186 4 @@ -13603,7 +13643,7 @@ 1 2 - 70979 + 70953 @@ -13619,47 +13659,47 @@ 1 2 - 20474 + 20467 2 3 - 10919 + 10915 3 4 - 6824 + 6822 4 7 - 5459 + 5457 7 10 - 5459 + 5457 10 28 - 5459 + 5457 37 71 - 5459 + 5457 71 253 - 5459 + 5457 459 951 - 5459 + 5457 @@ -13737,7 +13777,7 @@ 1 2 - 5459 + 5457 @@ -13848,17 +13888,17 @@ 1 2 - 2467893 + 2466984 2 3 - 1128842 + 1128427 3 5 - 128308 + 128261 @@ -13874,17 +13914,17 @@ 1 2 - 2467893 + 2466984 2 3 - 1128842 + 1128427 3 5 - 128308 + 128261 @@ -13900,17 +13940,17 @@ 1 2 - 2467893 + 2466984 2 3 - 1128842 + 1128427 3 5 - 128308 + 128261 @@ -13926,7 +13966,7 @@ 1 2 - 3725044 + 3723673 @@ -13936,19 +13976,19 @@ wildcards - 3116261 + 3637710 id - 3116261 + 3637710 nodeName - 859940 + 982427 kind - 2729 + 2728 @@ -13962,7 +14002,7 @@ 1 2 - 3116261 + 3637710 @@ -13978,7 +14018,7 @@ 1 2 - 3116261 + 3637710 @@ -13994,17 +14034,17 @@ 1 2 - 683857 + 791399 2 3 - 110563 + 120074 3 - 170 - 65519 + 214 + 70953 @@ -14020,7 +14060,7 @@ 1 2 - 859940 + 982427 @@ -14034,13 +14074,13 @@ 12 - 898 - 899 + 1027 + 1028 1364 - 1385 - 1386 + 1639 + 1640 1364 @@ -14055,13 +14095,13 @@ 12 - 196 - 197 + 222 + 223 1364 - 434 - 435 + 498 + 499 1364 @@ -14072,15 +14112,15 @@ typeBounds - 3872463 + 4393634 id - 3872463 + 4393634 typeid - 2723145 + 3191525 pos @@ -14088,7 +14128,7 @@ parentid - 3872463 + 4393634 @@ -14102,7 +14142,7 @@ 1 2 - 3872463 + 4393634 @@ -14118,7 +14158,7 @@ 1 2 - 3872463 + 4393634 @@ -14134,7 +14174,7 @@ 1 2 - 3872463 + 4393634 @@ -14150,17 +14190,17 @@ 1 2 - 2093887 + 2512012 2 3 - 552819 + 603101 3 - 51 - 76439 + 52 + 76411 @@ -14176,7 +14216,7 @@ 1 2 - 2723145 + 3191525 @@ -14192,17 +14232,17 @@ 1 2 - 2093887 + 2512012 2 3 - 552819 + 603101 3 - 51 - 76439 + 52 + 76411 @@ -14216,8 +14256,8 @@ 12 - 2837 - 2838 + 3220 + 3221 1364 @@ -14232,8 +14272,8 @@ 12 - 1995 - 1996 + 2339 + 2340 1364 @@ -14248,8 +14288,8 @@ 12 - 2837 - 2838 + 3220 + 3221 1364 @@ -14266,7 +14306,7 @@ 1 2 - 3872463 + 4393634 @@ -14282,7 +14322,7 @@ 1 2 - 3872463 + 4393634 @@ -14298,7 +14338,7 @@ 1 2 - 3872463 + 4393634 @@ -14604,37 +14644,37 @@ isParameterized - 24792227 + 25167883 memberid - 24792227 + 25167883 isRaw - 642908 + 642671 memberid - 642908 + 642671 erasure - 25435135 + 25810554 memberid - 25435135 + 25810554 erasureid - 868130 + 867810 @@ -14648,7 +14688,7 @@ 1 2 - 25435135 + 25810554 @@ -14664,57 +14704,57 @@ 1 2 - 144688 + 144635 2 3 - 133768 + 133719 3 4 - 88724 + 88691 4 5 - 55964 + 54579 5 6 - 51869 + 50485 6 8 - 62789 + 64130 8 11 - 77804 + 79139 11 19 - 70979 + 70953 19 33 - 70979 + 70953 33 93 - 65519 + 65495 97 - 1723 - 45044 + 1848 + 45027 @@ -14724,15 +14764,15 @@ isAnonymClass - 194713 + 195608 classid - 194713 + 195608 parent - 194713 + 195608 @@ -14746,7 +14786,7 @@ 1 2 - 194713 + 195608 @@ -14762,7 +14802,7 @@ 1 2 - 194713 + 195608 @@ -14772,15 +14812,15 @@ isLocalClassOrInterface - 4069 + 4110 typeid - 4069 + 4110 parent - 4069 + 4110 @@ -14794,7 +14834,7 @@ 1 2 - 4069 + 4110 @@ -14810,7 +14850,7 @@ 1 2 - 4069 + 4110 @@ -14831,15 +14871,15 @@ lambdaKind - 185816 + 186985 exprId - 185816 + 186985 bodyKind - 15 + 16 @@ -14853,7 +14893,7 @@ 1 2 - 185816 + 186985 @@ -14867,9 +14907,9 @@ 12 - 11987 - 11988 - 15 + 11644 + 11645 + 16 @@ -14879,27 +14919,27 @@ arrays - 1119287 + 1118875 id - 1119287 + 1118875 nodeName - 689317 + 689063 elementtypeid - 1112462 + 1112053 dimension - 2729 + 2728 componenttypeid - 1119287 + 1118875 @@ -14913,7 +14953,7 @@ 1 2 - 1119287 + 1118875 @@ -14929,7 +14969,7 @@ 1 2 - 1119287 + 1118875 @@ -14945,7 +14985,7 @@ 1 2 - 1119287 + 1118875 @@ -14961,7 +15001,7 @@ 1 2 - 1119287 + 1118875 @@ -14977,17 +15017,17 @@ 1 2 - 563738 + 563531 2 3 - 99643 + 99607 3 95 - 25934 + 25925 @@ -15003,17 +15043,17 @@ 1 2 - 563738 + 563531 2 3 - 99643 + 99607 3 95 - 25934 + 25925 @@ -15029,7 +15069,7 @@ 1 2 - 689317 + 689063 @@ -15045,17 +15085,17 @@ 1 2 - 563738 + 563531 2 3 - 99643 + 99607 3 95 - 25934 + 25925 @@ -15071,12 +15111,12 @@ 1 2 - 1105638 + 1105230 2 3 - 6824 + 6822 @@ -15092,12 +15132,12 @@ 1 2 - 1105638 + 1105230 2 3 - 6824 + 6822 @@ -15113,12 +15153,12 @@ 1 2 - 1105638 + 1105230 2 3 - 6824 + 6822 @@ -15134,12 +15174,12 @@ 1 2 - 1105638 + 1105230 2 3 - 6824 + 6822 @@ -15239,7 +15279,7 @@ 1 2 - 1119287 + 1118875 @@ -15255,7 +15295,7 @@ 1 2 - 1119287 + 1118875 @@ -15271,7 +15311,7 @@ 1 2 - 1119287 + 1118875 @@ -15287,7 +15327,7 @@ 1 2 - 1119287 + 1118875 @@ -15297,15 +15337,15 @@ enclInReftype - 3417923 + 3419393 child - 3417923 + 3419393 parent - 928189 + 927848 @@ -15319,7 +15359,7 @@ 1 2 - 3417923 + 3419393 @@ -15335,32 +15375,32 @@ 1 2 - 545994 + 545793 2 3 - 150148 + 150093 3 4 - 70979 + 70953 4 7 - 83264 + 83233 7 48 - 69614 + 69588 50 - 277 - 8189 + 279 + 8186 @@ -15370,15 +15410,15 @@ extendsReftype - 47612051 + 47979305 id1 - 33730150 + 34102515 id2 - 13910564 + 14050078 @@ -15392,22 +15432,22 @@ 1 2 - 25830981 + 26206254 2 3 - 3150385 + 3149226 3 4 - 3643145 + 3641804 4 10 - 1105638 + 1105230 @@ -15423,17 +15463,17 @@ 1 2 - 11822137 + 11956961 2 3 - 1423679 + 1429977 3 - 12114 - 664747 + 12283 + 663138 @@ -15526,15 +15566,15 @@ permits - 125 + 143 id1 - 32 + 36 id2 - 125 + 143 @@ -15548,12 +15588,12 @@ 1 2 - 2 + 1 2 3 - 9 + 12 3 @@ -15563,27 +15603,27 @@ 4 5 - 1 + 2 5 6 - 4 + 2 6 + 7 + 4 + + + 7 8 2 8 - 9 - 2 - - - 10 11 - 1 + 2 @@ -15599,7 +15639,7 @@ 1 2 - 125 + 143 @@ -15609,15 +15649,15 @@ hasModifier - 248786309 + 249505212 id1 - 166367134 + 166690663 id2 - 12284 + 13644 @@ -15631,17 +15671,17 @@ 1 2 - 84399769 + 84359142 2 3 - 81515555 + 81848494 3 4 - 451810 + 483026 @@ -15654,6 +15694,11 @@ 12 + + 31 + 32 + 1364 + 34 35 @@ -15680,13 +15725,13 @@ 1364 - 17754 - 17755 + 18033 + 18034 1364 - 18275 - 18276 + 18277 + 18278 1364 @@ -15695,8 +15740,8 @@ 1364 - 116552 - 116553 + 116834 + 116835 1364 @@ -16038,27 +16083,27 @@ stmts - 2534777 + 2533844 id - 2534777 + 2533844 kind - 13649 + 13644 parent - 1785400 + 1784743 idx - 217032 + 216952 bodydecl - 704332 + 704073 @@ -16072,7 +16117,7 @@ 1 2 - 2534777 + 2533844 @@ -16088,7 +16133,7 @@ 1 2 - 2534777 + 2533844 @@ -16104,7 +16149,7 @@ 1 2 - 2534777 + 2533844 @@ -16120,7 +16165,7 @@ 1 2 - 2534777 + 2533844 @@ -16136,7 +16181,7 @@ 2 3 - 4094 + 4093 4 @@ -16192,7 +16237,7 @@ 2 3 - 2729 + 2728 4 @@ -16243,7 +16288,7 @@ 1 2 - 5459 + 5457 2 @@ -16263,7 +16308,7 @@ 7 8 - 2729 + 2728 158 @@ -16289,7 +16334,7 @@ 2 3 - 4094 + 4093 25 @@ -16335,17 +16380,17 @@ 1 2 - 1502848 + 1502295 2 3 - 199287 + 199214 3 159 - 83264 + 83233 @@ -16361,17 +16406,17 @@ 1 2 - 1588842 + 1588257 2 3 - 189732 + 189663 3 4 - 6824 + 6822 @@ -16387,17 +16432,17 @@ 1 2 - 1502848 + 1502295 2 3 - 199287 + 199214 3 159 - 83264 + 83233 @@ -16413,7 +16458,7 @@ 1 2 - 1785400 + 1784743 @@ -16429,22 +16474,22 @@ 1 2 - 161068 + 161008 2 3 - 34124 + 34112 3 24 - 16379 + 16373 34 1211 - 5459 + 5457 @@ -16460,12 +16505,12 @@ 1 2 - 204747 + 204672 2 9 - 12284 + 12280 @@ -16481,22 +16526,22 @@ 1 2 - 161068 + 161008 2 3 - 34124 + 34112 3 24 - 16379 + 16373 34 1211 - 5459 + 5457 @@ -16512,22 +16557,22 @@ 1 2 - 161068 + 161008 2 3 - 34124 + 34112 3 19 - 16379 + 16373 29 517 - 5459 + 5457 @@ -16543,22 +16588,22 @@ 2 3 - 544629 + 544428 3 4 - 58694 + 58672 4 7 - 53234 + 53214 7 162 - 47774 + 47756 @@ -16574,17 +16619,17 @@ 2 3 - 576023 + 575811 3 4 - 81899 + 81868 4 7 - 46409 + 46392 @@ -16600,17 +16645,17 @@ 2 3 - 630623 + 630391 3 8 - 57329 + 57308 9 47 - 16379 + 16373 @@ -16626,22 +16671,22 @@ 1 2 - 544629 + 544428 2 3 - 92818 + 92784 3 7 - 54599 + 54579 7 159 - 12284 + 12280 @@ -17522,7 +17567,7 @@
    kttypeid - 12370 + 12368 @@ -17552,7 +17597,7 @@ 1 2 - 9277 + 9276 2 @@ -17560,8 +17605,8 @@ 2061 - 7177 - 7178 + 7178 + 7179 1030 @@ -17841,22 +17886,22 @@ when_if - 83447 + 84712 id - 83447 + 84712 when_branch_else - 80753 + 79813 id - 80753 + 79813 @@ -17901,12 +17946,12 @@ 1 2 - 162246 + 162245 2 3 - 33026 + 33027 3 @@ -17999,15 +18044,15 @@ propertyRefGetBinding - 9718 + 9660 id - 9718 + 9660 getter - 5873 + 5838 @@ -18021,7 +18066,7 @@ 1 2 - 9718 + 9660 @@ -18037,12 +18082,12 @@ 1 2 - 2132 + 2119 2 3 - 3646 + 3624 3 @@ -18105,15 +18150,15 @@ propertyRefSetBinding - 2651 + 2672 id - 2651 + 2672 setter - 1325 + 1336 @@ -18127,7 +18172,7 @@ 1 2 - 2651 + 2672 @@ -18143,7 +18188,7 @@ 2 3 - 1325 + 1336 @@ -18602,11 +18647,11 @@ localvarsKotlinType - 227623 + 227573 id - 227623 + 227573 kttypeid @@ -18624,7 +18669,7 @@ 1 2 - 227623 + 227573 @@ -20540,11 +20585,11 @@ xmlEncoding - 802611 + 802315 id - 802611 + 802315 encoding @@ -20562,7 +20607,7 @@ 1 2 - 802611 + 802315 @@ -20936,27 +20981,27 @@ xmlElements - 106792352 + 106753032 id - 106792352 + 106753032 name - 338516 + 338391 parentid - 2754540 + 2753526 idx - 1210741 + 1210296 fileid - 802611 + 802315 @@ -20970,7 +21015,7 @@ 1 2 - 106792352 + 106753032 @@ -20986,7 +21031,7 @@ 1 2 - 106792352 + 106753032 @@ -21002,7 +21047,7 @@ 1 2 - 106792352 + 106753032 @@ -21018,7 +21063,7 @@ 1 2 - 106792352 + 106753032 @@ -21034,57 +21079,57 @@ 1 2 - 106468 + 106429 2 3 - 40949 + 40934 3 4 - 16379 + 16373 4 6 - 30029 + 30018 6 8 - 24569 + 24560 8 9 - 12284 + 12280 9 10 - 23204 + 23196 10 18 - 28664 + 28654 18 48 - 25934 + 25925 52 250 - 25934 + 25925 342 73380 - 4094 + 4093 @@ -21100,52 +21145,52 @@ 1 2 - 124213 + 124167 2 3 - 46409 + 46392 3 4 - 17744 + 17738 4 5 - 15014 + 15009 5 6 - 19109 + 19102 6 8 - 28664 + 28654 8 10 - 28664 + 28654 10 21 - 27299 + 27289 22 128 - 25934 + 25925 130 229 - 5459 + 5457 @@ -21161,37 +21206,37 @@ 1 2 - 187002 + 186934 2 3 - 49139 + 49121 3 4 - 24569 + 24560 4 6 - 20474 + 20467 6 9 - 20474 + 20467 9 38 - 25934 + 25925 45 888 - 10919 + 10915 @@ -21207,42 +21252,42 @@ 1 2 - 184273 + 184205 2 3 - 36854 + 36841 3 4 - 17744 + 17738 4 5 - 13649 + 13644 5 7 - 30029 + 30018 7 16 - 25934 + 25925 17 114 - 27299 + 27289 118 131 - 2729 + 2728 @@ -21258,32 +21303,32 @@ 1 2 - 1676201 + 1675584 2 3 - 429970 + 429812 3 4 - 178813 + 178747 4 8 - 214302 + 214223 8 777 - 225222 + 225139 777 888 - 30029 + 30018 @@ -21299,17 +21344,17 @@ 1 2 - 2274065 + 2273228 2 3 - 292106 + 291999 3 17 - 188367 + 188298 @@ -21325,32 +21370,32 @@ 1 2 - 1676201 + 1675584 2 3 - 429970 + 429812 3 4 - 178813 + 178747 4 8 - 214302 + 214223 8 777 - 225222 + 225139 777 888 - 30029 + 30018 @@ -21366,7 +21411,7 @@ 1 2 - 2754540 + 2753526 @@ -21382,67 +21427,67 @@ 2 8 - 102373 + 102336 9 76 - 96913 + 96878 76 82 - 91454 + 91420 82 89 - 87359 + 87326 89 92 - 79169 + 79139 92 95 - 95548 + 95513 95 97 - 106468 + 106429 97 98 - 150148 + 150093 98 99 - 92818 + 92784 99 104 - 110563 + 110523 104 106 - 92818 + 92784 106 159 - 91454 + 91420 162 2019 - 13649 + 13644 @@ -21458,22 +21503,22 @@ 1 2 - 981424 + 981063 2 5 - 90089 + 90055 5 9 - 103738 + 103700 9 150 - 35489 + 35476 @@ -21489,67 +21534,67 @@ 2 8 - 102373 + 102336 9 76 - 96913 + 96878 76 82 - 91454 + 91420 82 89 - 87359 + 87326 89 92 - 79169 + 79139 92 95 - 95548 + 95513 95 97 - 106468 + 106429 97 98 - 150148 + 150093 98 99 - 92818 + 92784 99 104 - 110563 + 110523 104 106 - 92818 + 92784 106 159 - 91454 + 91420 162 2019 - 13649 + 13644 @@ -21565,67 +21610,67 @@ 2 8 - 102373 + 102336 9 76 - 96913 + 96878 76 82 - 91454 + 91420 82 89 - 87359 + 87326 89 92 - 79169 + 79139 92 95 - 95548 + 95513 95 97 - 106468 + 106429 97 98 - 150148 + 150093 98 99 - 92818 + 92784 99 104 - 110563 + 110523 104 106 - 92818 + 92784 106 139 - 91454 + 91420 141 589 - 13649 + 13644 @@ -21641,57 +21686,57 @@ 1 2 - 58694 + 58672 2 3 - 135133 + 135083 3 4 - 177448 + 177382 4 5 - 75074 + 75046 5 7 - 60059 + 60037 7 10 - 66884 + 66859 10 31 - 62789 + 62766 35 694 - 61424 + 61401 738 776 - 20474 + 20467 777 779 - 65519 + 65495 788 889 - 19109 + 19102 @@ -21707,37 +21752,37 @@ 1 2 - 58694 + 58672 2 3 - 399940 + 399793 3 4 - 139228 + 139177 4 5 - 65519 + 65495 5 6 - 61424 + 61401 6 9 - 65519 + 65495 9 69 - 12284 + 12280 @@ -21753,27 +21798,27 @@ 1 2 - 58694 + 58672 2 3 - 569198 + 568989 3 4 - 57329 + 57308 4 6 - 58694 + 58672 6 165 - 58694 + 58672 @@ -21789,42 +21834,42 @@ 1 2 - 203382 + 203307 2 3 - 219762 + 219681 3 4 - 88724 + 88691 4 7 - 66884 + 66859 7 17 - 66884 + 66859 18 763 - 61424 + 61401 764 777 - 65519 + 65495 777 888 - 30029 + 30018 @@ -21834,31 +21879,31 @@ xmlAttrs - 129898822 + 129850995 id - 129898822 + 129850995 elementid - 105883272 + 105844287 name - 513234 + 513045 value - 8206291 + 8203269 idx - 31394 + 31383 fileid - 801246 + 800951 @@ -21872,7 +21917,7 @@ 1 2 - 129898822 + 129850995 @@ -21888,7 +21933,7 @@ 1 2 - 129898822 + 129850995 @@ -21904,7 +21949,7 @@ 1 2 - 129898822 + 129850995 @@ -21920,7 +21965,7 @@ 1 2 - 129898822 + 129850995 @@ -21936,7 +21981,7 @@ 1 2 - 129898822 + 129850995 @@ -21952,17 +21997,17 @@ 1 2 - 96893479 + 96857804 2 6 - 7961959 + 7959027 6 24 - 1027833 + 1027455 @@ -21978,17 +22023,17 @@ 1 2 - 96893479 + 96857804 2 6 - 7975608 + 7972672 6 23 - 1014184 + 1013810 @@ -22004,17 +22049,17 @@ 1 2 - 96946713 + 96911018 2 6 - 8015193 + 8012242 6 21 - 921365 + 921025 @@ -22030,17 +22075,17 @@ 1 2 - 96893479 + 96857804 2 6 - 7961959 + 7959027 6 24 - 1027833 + 1027455 @@ -22056,7 +22101,7 @@ 1 2 - 105883272 + 105844287 @@ -22072,62 +22117,62 @@ 1 2 - 106468 + 106429 2 3 - 55964 + 55943 3 4 - 31394 + 31383 4 5 - 17744 + 17738 5 6 - 30029 + 30018 6 8 - 36854 + 36841 8 11 - 42314 + 42298 11 22 - 39584 + 39569 23 38 - 40949 + 40934 38 79 - 40949 + 40934 81 168 - 39584 + 39569 168 74700 - 31394 + 31383 @@ -22143,62 +22188,62 @@ 1 2 - 106468 + 106429 2 3 - 55964 + 55943 3 4 - 31394 + 31383 4 5 - 17744 + 17738 5 6 - 30029 + 30018 6 8 - 36854 + 36841 8 11 - 46409 + 46392 11 25 - 43679 + 43663 25 39 - 42314 + 42298 43 91 - 40949 + 40934 91 227 - 39584 + 39569 227 74700 - 21839 + 21831 @@ -22214,42 +22259,42 @@ 1 2 - 215667 + 215588 2 3 - 80534 + 80504 3 4 - 34124 + 34112 4 5 - 36854 + 36841 5 9 - 42314 + 42298 9 21 - 39584 + 39569 22 64 - 42314 + 42298 68 2100 - 21839 + 21831 @@ -22265,37 +22310,37 @@ 1 2 - 202017 + 201943 2 3 - 95548 + 95513 3 4 - 49139 + 49121 4 5 - 54599 + 54579 5 7 - 32759 + 32747 7 10 - 40949 + 40934 10 21 - 38219 + 38205 @@ -22311,52 +22356,52 @@ 1 2 - 178813 + 178747 2 3 - 53234 + 53214 3 4 - 27299 + 27289 4 5 - 24569 + 24560 5 6 - 38219 + 38205 6 9 - 42314 + 42298 9 17 - 45044 + 45027 17 34 - 39584 + 39569 36 91 - 39584 + 39569 91 223 - 24569 + 24560 @@ -22372,32 +22417,32 @@ 1 2 - 4482611 + 4480961 2 3 - 1213471 + 1213025 3 5 - 629258 + 629026 5 31 - 618338 + 618110 31 91 - 645638 + 645400 91 1111 - 615608 + 615381 3397 @@ -22418,32 +22463,32 @@ 1 2 - 4572700 + 4571017 2 3 - 1156142 + 1155716 3 5 - 637448 + 637213 5 33 - 647003 + 646764 33 93 - 633353 + 633119 93 3398 - 559643 + 559437 @@ -22459,17 +22504,17 @@ 1 2 - 7447359 + 7444617 2 4 - 660652 + 660409 4 53 - 98278 + 98242 @@ -22485,17 +22530,17 @@ 1 2 - 6796261 + 6793759 2 3 - 992344 + 991978 3 20 - 417685 + 417531 @@ -22511,32 +22556,32 @@ 1 2 - 5282492 + 5280548 2 3 - 889970 + 889642 3 10 - 637448 + 637213 10 83 - 623798 + 623568 83 99 - 626528 + 626297 99 182 - 146053 + 145999 @@ -22552,57 +22597,57 @@ 1 6 - 2729 + 2728 12 14 - 2729 + 2728 17 26 - 2729 + 2728 39 56 - 2729 + 2728 83 110 - 2729 + 2728 153 232 - 2729 + 2728 316 400 - 2729 + 2728 468 545 - 2729 + 2728 626 754 - 2729 + 2728 951 1491 - 2729 + 2728 4718 6587 - 2729 + 2728 77571 @@ -22623,57 +22668,57 @@ 1 6 - 2729 + 2728 12 14 - 2729 + 2728 17 26 - 2729 + 2728 39 56 - 2729 + 2728 83 110 - 2729 + 2728 153 232 - 2729 + 2728 316 400 - 2729 + 2728 468 545 - 2729 + 2728 626 754 - 2729 + 2728 951 1491 - 2729 + 2728 4718 6587 - 2729 + 2728 77571 @@ -22694,57 +22739,57 @@ 1 4 - 2729 + 2728 7 10 - 2729 + 2728 11 17 - 2729 + 2728 18 23 - 2729 + 2728 26 38 - 2729 + 2728 39 49 - 2729 + 2728 57 67 - 2729 + 2728 72 79 - 2729 + 2728 95 101 - 2729 + 2728 105 106 - 2729 + 2728 106 132 - 2729 + 2728 140 @@ -22765,57 +22810,57 @@ 1 5 - 2729 + 2728 7 10 - 2729 + 2728 11 18 - 2729 + 2728 22 32 - 2729 + 2728 46 63 - 2729 + 2728 85 119 - 2729 + 2728 142 185 - 2729 + 2728 212 228 - 2729 + 2728 253 275 - 2729 + 2728 307 423 - 2729 + 2728 580 1324 - 2729 + 2728 3579 @@ -22836,57 +22881,57 @@ 1 6 - 2729 + 2728 7 8 - 2729 + 2728 10 19 - 2729 + 2728 23 36 - 2729 + 2728 45 59 - 2729 + 2728 73 97 - 2729 + 2728 115 131 - 2729 + 2728 140 148 - 2729 + 2728 168 181 - 2729 + 2728 248 363 - 2729 + 2728 473 530 - 2729 + 2728 587 @@ -22907,72 +22952,72 @@ 1 3 - 60059 + 60037 3 5 - 61424 + 61401 5 6 - 36854 + 36841 6 7 - 61424 + 61401 7 8 - 51869 + 51850 8 10 - 58694 + 58672 10 15 - 65519 + 65495 15 27 - 61424 + 61401 27 41 - 61424 + 61401 41 65 - 61424 + 61401 65 157 - 65519 + 65495 162 817 - 61424 + 61401 818 832 - 66884 + 66859 832 1187 - 27299 + 27289 @@ -22988,52 +23033,52 @@ 1 2 - 91454 + 91420 2 3 - 188367 + 188298 3 4 - 113293 + 113252 4 5 - 77804 + 77775 5 8 - 72344 + 72317 8 14 - 61424 + 61401 14 295 - 61424 + 61401 330 775 - 50504 + 50485 776 778 - 65519 + 65495 787 888 - 19109 + 19102 @@ -23049,62 +23094,62 @@ 1 2 - 50504 + 50485 2 3 - 65519 + 65495 3 4 - 50504 + 50485 4 5 - 66884 + 66859 5 6 - 121483 + 121438 6 7 - 114658 + 114616 7 8 - 50504 + 50485 8 12 - 61424 + 61401 12 18 - 69614 + 69588 18 24 - 68249 + 68224 24 37 - 62789 + 62766 37 55 - 19109 + 19102 @@ -23120,67 +23165,67 @@ 1 3 - 69614 + 69588 3 4 - 39584 + 39569 4 5 - 73709 + 73682 5 6 - 88724 + 88691 6 8 - 62789 + 62766 8 12 - 70979 + 70953 12 19 - 61424 + 61401 19 27 - 69614 + 69588 27 41 - 61424 + 61401 42 170 - 61424 + 61401 205 780 - 57329 + 57308 781 783 - 65519 + 65495 791 893 - 19109 + 19102 @@ -23196,47 +23241,47 @@ 1 2 - 79169 + 79139 2 3 - 76439 + 76411 3 4 - 151513 + 151457 4 5 - 155608 + 155551 5 6 - 92818 + 92784 6 10 - 68249 + 68224 10 12 - 46409 + 46392 12 15 - 69614 + 69588 15 24 - 61424 + 61401 @@ -23246,23 +23291,23 @@ xmlNs - 1287181 + 1286707 id - 8189 + 8186 prefixName - 9554 + 9551 URI - 8189 + 8186 fileid - 749376 + 749100 @@ -23276,7 +23321,7 @@ 1 2 - 6824 + 6822 2 @@ -23297,7 +23342,7 @@ 1 2 - 8189 + 8186 @@ -23354,7 +23399,7 @@ 1 2 - 9554 + 9551 @@ -23370,7 +23415,7 @@ 1 2 - 9554 + 9551 @@ -23432,7 +23477,7 @@ 1 2 - 8189 + 8186 @@ -23448,7 +23493,7 @@ 1 2 - 6824 + 6822 2 @@ -23510,17 +23555,17 @@ 1 2 - 334421 + 334298 2 3 - 292106 + 291999 3 4 - 122848 + 122803 @@ -23536,17 +23581,17 @@ 1 2 - 334421 + 334298 2 3 - 292106 + 291999 3 4 - 122848 + 122803 @@ -23562,17 +23607,17 @@ 1 2 - 334421 + 334298 2 3 - 292106 + 291999 3 4 - 122848 + 122803 @@ -23582,19 +23627,19 @@ xmlHasNs - 25966114 + 25956554 elementId - 25966114 + 25956554 nsId - 8189 + 8186 fileid - 745281 + 745007 @@ -23608,7 +23653,7 @@ 1 2 - 25966114 + 25956554 @@ -23624,7 +23669,7 @@ 1 2 - 25966114 + 25956554 @@ -23722,77 +23767,77 @@ 1 3 - 45044 + 45027 3 5 - 62789 + 62766 5 6 - 34124 + 34112 6 7 - 65519 + 65495 7 8 - 49139 + 49121 8 10 - 61424 + 61401 10 15 - 65519 + 65495 15 25 - 55964 + 55943 25 36 - 57329 + 57308 36 49 - 58694 + 58672 49 54 - 15014 + 15009 54 55 - 58694 + 58672 55 81 - 57329 + 57308 81 298 - 55964 + 55943 298 833 - 2729 + 2728 @@ -23808,17 +23853,17 @@ 1 2 - 335786 + 335662 2 3 - 289376 + 289270 3 4 - 120118 + 120074 @@ -23828,23 +23873,23 @@ xmlComments - 107485764 + 107446189 id - 107485764 + 107446189 text - 1696676 + 1696051 parentid - 842195 + 841885 fileid - 787596 + 787306 @@ -23858,7 +23903,7 @@ 1 2 - 107485764 + 107446189 @@ -23874,7 +23919,7 @@ 1 2 - 107485764 + 107446189 @@ -23890,7 +23935,7 @@ 1 2 - 107485764 + 107446189 @@ -23906,67 +23951,67 @@ 1 2 - 233412 + 233326 2 7 - 139228 + 139177 7 32 - 140593 + 140541 32 61 - 128308 + 128261 61 76 - 128308 + 128261 76 84 - 136498 + 136448 84 90 - 125578 + 125532 90 94 - 111928 + 111887 94 95 - 60059 + 60037 95 96 - 101008 + 100971 96 98 - 140593 + 140541 98 100 - 126943 + 126896 100 460 - 124213 + 124167 @@ -23982,67 +24027,67 @@ 1 2 - 236142 + 236055 2 6 - 135133 + 135083 6 32 - 143323 + 143270 32 61 - 129673 + 129625 61 75 - 132403 + 132354 75 84 - 148783 + 148728 84 90 - 122848 + 122803 90 94 - 120118 + 120074 94 95 - 66884 + 66859 95 96 - 103738 + 103700 96 98 - 143323 + 143270 98 100 - 135133 + 135083 100 460 - 79169 + 79139 @@ -24058,67 +24103,67 @@ 1 2 - 247062 + 246971 2 7 - 133768 + 133719 7 32 - 133768 + 133719 32 61 - 129673 + 129625 61 75 - 132403 + 132354 75 84 - 148783 + 148728 84 90 - 122848 + 122803 90 94 - 120118 + 120074 94 95 - 66884 + 66859 95 96 - 103738 + 103700 96 98 - 143323 + 143270 98 100 - 135133 + 135083 100 460 - 79169 + 79139 @@ -24134,22 +24179,22 @@ 1 2 - 670207 + 669961 2 724 - 64154 + 64130 726 830 - 77804 + 77775 831 941 - 30029 + 30018 @@ -24165,27 +24210,27 @@ 1 2 - 670207 + 669961 2 697 - 64154 + 64130 697 795 - 34124 + 34112 795 827 - 64154 + 64130 838 899 - 9554 + 9551 @@ -24201,7 +24246,7 @@ 1 2 - 842195 + 841885 @@ -24217,27 +24262,27 @@ 1 2 - 601958 + 601736 2 549 - 60059 + 60037 579 829 - 40949 + 40934 829 832 - 65519 + 65495 834 941 - 19109 + 19102 @@ -24253,27 +24298,27 @@ 1 2 - 601958 + 601736 2 536 - 60059 + 60037 560 795 - 51869 + 51850 795 812 - 60059 + 60037 819 899 - 13649 + 13644 @@ -24289,12 +24334,12 @@ 1 2 - 748011 + 747736 2 6 - 39584 + 39569 @@ -24304,19 +24349,19 @@ xmlChars - 101574013 + 101536615 id - 101574013 + 101536615 text - 78006178 + 77977457 parentid - 101574013 + 101536615 idx @@ -24324,11 +24369,11 @@ isCDATA - 2729 + 2728 fileid - 177448 + 177382 @@ -24342,7 +24387,7 @@ 1 2 - 101574013 + 101536615 @@ -24358,7 +24403,7 @@ 1 2 - 101574013 + 101536615 @@ -24374,7 +24419,7 @@ 1 2 - 101574013 + 101536615 @@ -24390,7 +24435,7 @@ 1 2 - 101574013 + 101536615 @@ -24406,7 +24451,7 @@ 1 2 - 101574013 + 101536615 @@ -24422,17 +24467,17 @@ 1 2 - 66746414 + 66721839 2 3 - 7010564 + 7007983 3 128 - 4249199 + 4247634 @@ -24448,17 +24493,17 @@ 1 2 - 66746414 + 66721839 2 3 - 7010564 + 7007983 3 128 - 4249199 + 4247634 @@ -24474,7 +24519,7 @@ 1 2 - 78006178 + 77977457 @@ -24490,7 +24535,7 @@ 1 2 - 78006178 + 77977457 @@ -24506,12 +24551,12 @@ 1 2 - 74636029 + 74608549 2 76 - 3370148 + 3368907 @@ -24527,7 +24572,7 @@ 1 2 - 101574013 + 101536615 @@ -24543,7 +24588,7 @@ 1 2 - 101574013 + 101536615 @@ -24559,7 +24604,7 @@ 1 2 - 101574013 + 101536615 @@ -24575,7 +24620,7 @@ 1 2 - 101574013 + 101536615 @@ -24591,7 +24636,7 @@ 1 2 - 101574013 + 101536615 @@ -24750,7 +24795,7 @@ 1 2 - 2729 + 2728 @@ -24787,57 +24832,57 @@ 1 2 - 15014 + 15009 2 23 - 13649 + 13644 24 243 - 13649 + 13644 294 566 - 13649 + 13644 610 686 - 13649 + 13644 691 764 - 13649 + 13644 765 775 - 13649 + 13644 775 776 - 4094 + 4093 776 777 - 49139 + 49121 777 803 - 13649 + 13644 807 888 - 13649 + 13644 @@ -24853,67 +24898,67 @@ 1 2 - 15014 + 15009 2 21 - 13649 + 13644 22 188 - 13649 + 13644 208 492 - 13649 + 13644 525 589 - 13649 + 13644 590 638 - 13649 + 13644 639 651 - 13649 + 13644 652 656 - 12284 + 12280 656 659 - 16379 + 16373 659 663 - 15014 + 15009 663 667 - 13649 + 13644 667 701 - 13649 + 13644 702 744 - 9554 + 9551 @@ -24929,57 +24974,57 @@ 1 2 - 15014 + 15009 2 23 - 13649 + 13644 24 243 - 13649 + 13644 294 566 - 13649 + 13644 610 686 - 13649 + 13644 691 764 - 13649 + 13644 765 775 - 13649 + 13644 775 776 - 4094 + 4093 776 777 - 49139 + 49121 777 803 - 13649 + 13644 807 888 - 13649 + 13644 @@ -24995,7 +25040,7 @@ 1 2 - 177448 + 177382 @@ -25011,12 +25056,12 @@ 1 2 - 43679 + 43663 2 3 - 133768 + 133719 @@ -25026,15 +25071,15 @@ xmllocations - 447840746 + 447675856 xmlElement - 446561755 + 446397336 location - 419653800 + 419499288 @@ -25048,12 +25093,12 @@ 1 2 - 446553565 + 446389149 2 454 - 8189 + 8186 @@ -25069,12 +25114,12 @@ 1 2 - 410141218 + 409990208 2 25 - 9512582 + 9509079 @@ -25315,19 +25360,19 @@ ktComments - 133768 + 133719 id - 133768 + 133719 kind - 4094 + 4093 text - 96913 + 96878 @@ -25341,7 +25386,7 @@ 1 2 - 133768 + 133719 @@ -25357,7 +25402,7 @@ 1 2 - 133768 + 133719 @@ -25425,12 +25470,12 @@ 1 2 - 92818 + 92784 4 23 - 4094 + 4093 @@ -25446,7 +25491,7 @@ 1 2 - 96913 + 96878 @@ -25456,19 +25501,19 @@ ktCommentSections - 59246 + 59191 id - 59246 + 59191 comment - 54224 + 54069 content - 50410 + 50263 @@ -25482,7 +25527,7 @@ 1 2 - 59246 + 59191 @@ -25498,7 +25543,7 @@ 1 2 - 59246 + 59191 @@ -25514,12 +25559,12 @@ 1 2 - 52208 + 52013 2 18 - 2015 + 2055 @@ -25535,12 +25580,12 @@ 1 2 - 52208 + 52013 2 18 - 2015 + 2055 @@ -25556,17 +25601,17 @@ 1 2 - 44628 + 44482 2 3 - 4836 + 4801 3 63 - 945 + 979 @@ -25582,17 +25627,17 @@ 1 2 - 44737 + 44594 2 3 - 4743 + 4705 3 56 - 930 + 963 @@ -25602,15 +25647,15 @@ ktCommentSectionNames - 5022 + 5122 id - 5022 + 5122 name - 15 + 16 @@ -25624,7 +25669,7 @@ 1 2 - 5022 + 5122 @@ -25638,9 +25683,9 @@ 12 - 324 - 325 - 15 + 319 + 320 + 16 @@ -25650,15 +25695,15 @@ ktCommentSectionSubjectNames - 5022 + 5122 id - 5022 + 5122 subjectname - 3301 + 3388 @@ -25672,7 +25717,7 @@ 1 2 - 5022 + 5122 @@ -25688,22 +25733,22 @@ 1 2 - 2511 + 2601 2 3 - 496 + 497 3 - 9 - 248 + 10 + 256 - 10 - 15 - 46 + 13 + 16 + 32 @@ -25713,15 +25758,15 @@ ktCommentOwners - 84203 + 82540 id - 53480 + 53314 owner - 82173 + 80437 @@ -25735,22 +25780,22 @@ 1 2 - 34335 + 34638 2 3 - 12184 + 12076 3 4 - 4526 + 4496 4 6 - 2433 + 2103 @@ -25766,12 +25811,12 @@ 1 2 - 80142 + 78333 2 3 - 2030 + 2103 @@ -25781,15 +25826,15 @@ ktExtensionFunctions - 702967 + 702708 id - 702967 + 702708 typeid - 84629 + 84597 kttypeid @@ -25807,7 +25852,7 @@ 1 2 - 702967 + 702708 @@ -25823,7 +25868,7 @@ 1 2 - 702967 + 702708 @@ -25839,12 +25884,12 @@ 1 2 - 53234 + 53214 2 3 - 6824 + 6822 3 @@ -25854,22 +25899,22 @@ 4 5 - 6824 + 6822 5 12 - 6824 + 6822 12 69 - 6824 + 6822 84 174 - 2729 + 2728 @@ -25885,7 +25930,7 @@ 1 2 - 84629 + 84597 @@ -25927,15 +25972,15 @@ ktProperties - 30317687 + 30306525 id - 30317687 + 30306525 nodeName - 10696024 + 10692086 @@ -25949,7 +25994,7 @@ 1 2 - 30317687 + 30306525 @@ -25965,22 +26010,22 @@ 1 2 - 7889614 + 7886709 2 3 - 1216201 + 1215754 3 8 - 809436 + 809138 8 554 - 780771 + 780484 @@ -25990,15 +26035,15 @@ ktPropertyGetters - 4569970 + 4568288 id - 4569970 + 4568288 getter - 4569970 + 4568288 @@ -26012,7 +26057,7 @@ 1 2 - 4569970 + 4568288 @@ -26028,7 +26073,7 @@ 1 2 - 4569970 + 4568288 @@ -26038,15 +26083,15 @@ ktPropertySetters - 290741 + 290634 id - 290741 + 290634 setter - 290741 + 290634 @@ -26060,7 +26105,7 @@ 1 2 - 290741 + 290634 @@ -26076,7 +26121,7 @@ 1 2 - 290741 + 290634 @@ -26086,15 +26131,15 @@ ktPropertyBackingFields - 23615610 + 23606915 id - 23615610 + 23606915 backingField - 23615610 + 23606915 @@ -26108,7 +26153,7 @@ 1 2 - 23615610 + 23606915 @@ -26124,7 +26169,7 @@ 1 2 - 23615610 + 23606915 @@ -26134,11 +26179,11 @@ ktSyntheticBody - 10308 + 10307 id - 10308 + 10307 kind @@ -26156,7 +26201,7 @@ 1 2 - 10308 + 10307 @@ -26182,37 +26227,37 @@ ktLocalFunction - 2729 + 2728 id - 2729 + 2728 ktInitializerAssignment - 393115 + 392971 id - 393115 + 392971 ktPropertyDelegates - 5698 + 5664 id - 5698 + 5664 variableId - 5698 + 5664 @@ -26226,7 +26271,7 @@ 1 2 - 5698 + 5664 @@ -26242,7 +26287,7 @@ 1 2 - 5698 + 5664 @@ -26252,15 +26297,15 @@ compiler_generated - 120 + 491650 id - 120 + 491650 kind - 2 + 5153 @@ -26274,7 +26319,7 @@ 1 2 - 120 + 491650 @@ -26288,9 +26333,29 @@ 12 - 42 - 43 - 2 + 2 + 3 + 1030 + + + 5 + 6 + 1030 + + + 10 + 11 + 1030 + + + 184 + 185 + 1030 + + + 276 + 277 + 1030 @@ -26300,15 +26365,15 @@ ktFunctionOriginalNames - 981424 + 1147529 id - 981424 + 1147529 name - 66884 + 79139 @@ -26322,7 +26387,7 @@ 1 2 - 981424 + 1147529 @@ -26338,22 +26403,27 @@ 1 2 - 53234 + 55943 2 - 3 - 5459 + 4 + 6822 - 3 - 96 - 5459 + 7 + 14 + 4093 - 96 + 16 + 31 + 6822 + + + 92 380 - 2729 + 5457 From 28a8999b7472cf8d1feba71e7576d32a165446a3 Mon Sep 17 00:00:00 2001 From: Ian Lynagh Date: Fri, 1 Jul 2022 12:42:15 +0100 Subject: [PATCH 166/505] Java: Add an upgrade script --- .../old.dbscheme | 1228 ++++++++++++++++ .../semmlecode.dbscheme | 1236 +++++++++++++++++ .../upgrade.properties | 2 + 3 files changed, 2466 insertions(+) create mode 100644 java/ql/lib/upgrades/cf58c7d9b1fa1eae9cdc20ce8f157c140ac0c3de/old.dbscheme create mode 100755 java/ql/lib/upgrades/cf58c7d9b1fa1eae9cdc20ce8f157c140ac0c3de/semmlecode.dbscheme create mode 100644 java/ql/lib/upgrades/cf58c7d9b1fa1eae9cdc20ce8f157c140ac0c3de/upgrade.properties diff --git a/java/ql/lib/upgrades/cf58c7d9b1fa1eae9cdc20ce8f157c140ac0c3de/old.dbscheme b/java/ql/lib/upgrades/cf58c7d9b1fa1eae9cdc20ce8f157c140ac0c3de/old.dbscheme new file mode 100644 index 00000000000..cf58c7d9b1f --- /dev/null +++ b/java/ql/lib/upgrades/cf58c7d9b1fa1eae9cdc20ce8f157c140ac0c3de/old.dbscheme @@ -0,0 +1,1228 @@ +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * javac A.java B.java C.java + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + /** + * An invocation of the compiler. Note that more than one file may + * be compiled per invocation. For example, this command compiles + * three source files: + * + * javac A.java B.java C.java + */ + unique int id : @compilation, + int kind: int ref, + string cwd : string ref, + string name : string ref +); + +case @compilation.kind of + 1 = @javacompilation +| 2 = @kotlincompilation +; + +compilation_started( + int id : @compilation ref +) + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * javac A.java B.java C.java + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | *path to extractor* + * 1 | `--javac-args` + * 2 | A.java + * 3 | B.java + * 4 | C.java + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * javac A.java B.java C.java + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | A.java + * 1 | B.java + * 2 | C.java + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * For each file recorded in `compilation_compiling_files`, + * there will be a corresponding row in + * `compilation_compiling_files_completed` once extraction + * of that file is complete. The `result` will indicate the + * extraction result: + * + * 0: Successfully extracted + * 1: Errors were encountered, but extraction recovered + * 2: Errors were encountered, and extraction could not recover + */ +#keyset[id, num] +compilation_compiling_files_completed( + int id : @compilation ref, + int num : int ref, + int result : int ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + unique int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +/** + * The `cpu_seconds` and `elapsed_seconds` are the CPU time and elapsed + * time (respectively) that the original compilation (not the extraction) + * took for compiler invocation `id`. + */ +compilation_compiler_times( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + * The `result` will indicate the extraction result: + * + * 0: Successfully extracted + * 1: Errors were encountered, but extraction recovered + * 2: Errors were encountered, and extraction could not recover + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref, + int result : int ref +); + +diagnostics( + unique int id: @diagnostic, + string generated_by: string ref, // TODO: Sync this with the other languages? + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +/* + * External artifacts + */ + +externalData( + int id : @externalDataElement, + string path : string ref, + int column: int ref, + string value : string ref +); + +snapshotDate( + unique date snapshotDate : date ref +); + +sourceLocationPrefix( + string prefix : string ref +); + +/* + * Duplicate code + */ + +duplicateCode( + unique int id : @duplication, + string relativePath : string ref, + int equivClass : int ref +); + +similarCode( + unique int id : @similarity, + string relativePath : string ref, + int equivClass : int ref +); + +@duplication_or_similarity = @duplication | @similarity + +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref +); + +/* + * SMAP + */ + +smap_header( + int outputFileId: @file ref, + string outputFilename: string ref, + string defaultStratum: string ref +); + +smap_files( + int outputFileId: @file ref, + string stratum: string ref, + int inputFileNum: int ref, + string inputFileName: string ref, + int inputFileId: @file ref +); + +smap_lines( + int outputFileId: @file ref, + string stratum: string ref, + int inputFileNum: int ref, + int inputStartLine: int ref, + int inputLineCount: int ref, + int outputStartLine: int ref, + int outputLineIncrement: int ref +); + +/* + * Locations and files + */ + +@location = @location_default ; + +locations_default( + unique int id: @location_default, + int file: @file ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +hasLocation( + int locatableid: @locatable ref, + int id: @location ref +); + +@sourceline = @locatable ; + +#keyset[element_id] +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +files( + unique int id: @file, + string name: string ref +); + +folders( + unique int id: @folder, + string name: string ref +); + +@container = @folder | @file + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +/* + * Java + */ + +cupackage( + unique int id: @file ref, + int packageid: @package ref +); + +#keyset[fileid,keyName] +jarManifestMain( + int fileid: @file ref, + string keyName: string ref, + string value: string ref +); + +#keyset[fileid,entryName,keyName] +jarManifestEntries( + int fileid: @file ref, + string entryName: string ref, + string keyName: string ref, + string value: string ref +); + +packages( + unique int id: @package, + string nodeName: string ref +); + +primitives( + unique int id: @primitive, + string nodeName: string ref +); + +modifiers( + unique int id: @modifier, + string nodeName: string ref +); + +classes( + unique int id: @class, + string nodeName: string ref, + int parentid: @package ref, + int sourceid: @class ref +); + +file_class( + int id: @class ref +); + +class_object( + unique int id: @class ref, + unique int instance: @field ref +); + +type_companion_object( + unique int id: @classorinterface ref, + unique int instance: @field ref, + unique int companion_object: @class ref +); + +kt_nullable_types( + unique int id: @kt_nullable_type, + int classid: @reftype ref +) + +kt_notnull_types( + unique int id: @kt_notnull_type, + int classid: @reftype ref +) + +kt_type_alias( + unique int id: @kt_type_alias, + string name: string ref, + int kttypeid: @kt_type ref +) + +@kt_type = @kt_nullable_type | @kt_notnull_type + +isRecord( + unique int id: @class ref +); + +interfaces( + unique int id: @interface, + string nodeName: string ref, + int parentid: @package ref, + int sourceid: @interface ref +); + +fielddecls( + unique int id: @fielddecl, + int parentid: @reftype ref +); + +#keyset[fieldId] #keyset[fieldDeclId,pos] +fieldDeclaredIn( + int fieldId: @field ref, + int fieldDeclId: @fielddecl ref, + int pos: int ref +); + +fields( + unique int id: @field, + string nodeName: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @field ref +); + +fieldsKotlinType( + unique int id: @field ref, + int kttypeid: @kt_type ref +); + +constrs( + unique int id: @constructor, + string nodeName: string ref, + string signature: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @constructor ref +); + +constrsKotlinType( + unique int id: @constructor ref, + int kttypeid: @kt_type ref +); + +methods( + unique int id: @method, + string nodeName: string ref, + string signature: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @method ref +); + +methodsKotlinType( + unique int id: @method ref, + int kttypeid: @kt_type ref +); + +#keyset[parentid,pos] +params( + unique int id: @param, + int typeid: @type ref, + int pos: int ref, + int parentid: @callable ref, + int sourceid: @param ref +); + +paramsKotlinType( + unique int id: @param ref, + int kttypeid: @kt_type ref +); + +paramName( + unique int id: @param ref, + string nodeName: string ref +); + +isVarargsParam( + int param: @param ref +); + +exceptions( + unique int id: @exception, + int typeid: @type ref, + int parentid: @callable ref +); + +isAnnotType( + int interfaceid: @interface ref +); + +isAnnotElem( + int methodid: @method ref +); + +annotValue( + int parentid: @annotation ref, + int id2: @method ref, + unique int value: @expr ref +); + +isEnumType( + int classid: @class ref +); + +isEnumConst( + int fieldid: @field ref +); + +#keyset[parentid,pos] +typeVars( + unique int id: @typevariable, + string nodeName: string ref, + int pos: int ref, + int kind: int ref, // deprecated + int parentid: @classorinterfaceorcallable ref +); + +wildcards( + unique int id: @wildcard, + string nodeName: string ref, + int kind: int ref +); + +#keyset[parentid,pos] +typeBounds( + unique int id: @typebound, + int typeid: @reftype ref, + int pos: int ref, + int parentid: @boundedtype ref +); + +#keyset[parentid,pos] +typeArgs( + int argumentid: @reftype ref, + int pos: int ref, + int parentid: @classorinterfaceorcallable ref +); + +isParameterized( + int memberid: @member ref +); + +isRaw( + int memberid: @member ref +); + +erasure( + unique int memberid: @member ref, + int erasureid: @member ref +); + +#keyset[classid] #keyset[parent] +isAnonymClass( + int classid: @class ref, + int parent: @classinstancexpr ref +); + +#keyset[typeid] #keyset[parent] +isLocalClassOrInterface( + int typeid: @classorinterface ref, + int parent: @localtypedeclstmt ref +); + +isDefConstr( + int constructorid: @constructor ref +); + +#keyset[exprId] +lambdaKind( + int exprId: @lambdaexpr ref, + int bodyKind: int ref +); + +arrays( + unique int id: @array, + string nodeName: string ref, + int elementtypeid: @type ref, + int dimension: int ref, + int componenttypeid: @type ref +); + +enclInReftype( + unique int child: @reftype ref, + int parent: @reftype ref +); + +extendsReftype( + int id1: @reftype ref, + int id2: @classorinterface ref +); + +implInterface( + int id1: @classorarray ref, + int id2: @interface ref +); + +permits( + int id1: @classorinterface ref, + int id2: @classorinterface ref +); + +hasModifier( + int id1: @modifiable ref, + int id2: @modifier ref +); + +imports( + unique int id: @import, + int holder: @classorinterfaceorpackage ref, + string name: string ref, + int kind: int ref +); + +#keyset[parent,idx] +stmts( + unique int id: @stmt, + int kind: int ref, + int parent: @stmtparent ref, + int idx: int ref, + int bodydecl: @callable ref +); + +@stmtparent = @callable | @stmt | @switchexpr | @whenexpr| @stmtexpr; + +case @stmt.kind of + 0 = @block +| 1 = @ifstmt +| 2 = @forstmt +| 3 = @enhancedforstmt +| 4 = @whilestmt +| 5 = @dostmt +| 6 = @trystmt +| 7 = @switchstmt +| 8 = @synchronizedstmt +| 9 = @returnstmt +| 10 = @throwstmt +| 11 = @breakstmt +| 12 = @continuestmt +| 13 = @emptystmt +| 14 = @exprstmt +| 15 = @labeledstmt +| 16 = @assertstmt +| 17 = @localvariabledeclstmt +| 18 = @localtypedeclstmt +| 19 = @constructorinvocationstmt +| 20 = @superconstructorinvocationstmt +| 21 = @case +| 22 = @catchclause +| 23 = @yieldstmt +| 24 = @errorstmt +| 25 = @whenbranch +; + +#keyset[parent,idx] +exprs( + unique int id: @expr, + int kind: int ref, + int typeid: @type ref, + int parent: @exprparent ref, + int idx: int ref +); + +exprsKotlinType( + unique int id: @expr ref, + int kttypeid: @kt_type ref +); + +callableEnclosingExpr( + unique int id: @expr ref, + int callable_id: @callable ref +); + +statementEnclosingExpr( + unique int id: @expr ref, + int statement_id: @stmt ref +); + +isParenthesized( + unique int id: @expr ref, + int parentheses: int ref +); + +case @expr.kind of + 1 = @arrayaccess +| 2 = @arraycreationexpr +| 3 = @arrayinit +| 4 = @assignexpr +| 5 = @assignaddexpr +| 6 = @assignsubexpr +| 7 = @assignmulexpr +| 8 = @assigndivexpr +| 9 = @assignremexpr +| 10 = @assignandexpr +| 11 = @assignorexpr +| 12 = @assignxorexpr +| 13 = @assignlshiftexpr +| 14 = @assignrshiftexpr +| 15 = @assignurshiftexpr +| 16 = @booleanliteral +| 17 = @integerliteral +| 18 = @longliteral +| 19 = @floatingpointliteral +| 20 = @doubleliteral +| 21 = @characterliteral +| 22 = @stringliteral +| 23 = @nullliteral +| 24 = @mulexpr +| 25 = @divexpr +| 26 = @remexpr +| 27 = @addexpr +| 28 = @subexpr +| 29 = @lshiftexpr +| 30 = @rshiftexpr +| 31 = @urshiftexpr +| 32 = @andbitexpr +| 33 = @orbitexpr +| 34 = @xorbitexpr +| 35 = @andlogicalexpr +| 36 = @orlogicalexpr +| 37 = @ltexpr +| 38 = @gtexpr +| 39 = @leexpr +| 40 = @geexpr +| 41 = @eqexpr +| 42 = @neexpr +| 43 = @postincexpr +| 44 = @postdecexpr +| 45 = @preincexpr +| 46 = @predecexpr +| 47 = @minusexpr +| 48 = @plusexpr +| 49 = @bitnotexpr +| 50 = @lognotexpr +| 51 = @castexpr +| 52 = @newexpr +| 53 = @conditionalexpr +| 54 = @parexpr // deprecated +| 55 = @instanceofexpr +| 56 = @localvariabledeclexpr +| 57 = @typeliteral +| 58 = @thisaccess +| 59 = @superaccess +| 60 = @varaccess +| 61 = @methodaccess +| 62 = @unannotatedtypeaccess +| 63 = @arraytypeaccess +| 64 = @packageaccess +| 65 = @wildcardtypeaccess +| 66 = @declannotation +| 67 = @uniontypeaccess +| 68 = @lambdaexpr +| 69 = @memberref +| 70 = @annotatedtypeaccess +| 71 = @typeannotation +| 72 = @intersectiontypeaccess +| 73 = @switchexpr +| 74 = @errorexpr +| 75 = @whenexpr +| 76 = @getclassexpr +| 77 = @safecastexpr +| 78 = @implicitcastexpr +| 79 = @implicitnotnullexpr +| 80 = @implicitcoerciontounitexpr +| 81 = @notinstanceofexpr +| 82 = @stmtexpr +| 83 = @stringtemplateexpr +| 84 = @notnullexpr +| 85 = @unsafecoerceexpr +| 86 = @valueeqexpr +| 87 = @valueneexpr +| 88 = @propertyref +; + +/** Holds if this `when` expression was written as an `if` expression. */ +when_if(unique int id: @whenexpr ref); + +/** Holds if this `when` branch was written as an `else` branch. */ +when_branch_else(unique int id: @whenbranch ref); + +@classinstancexpr = @newexpr | @lambdaexpr | @memberref | @propertyref + +@annotation = @declannotation | @typeannotation +@typeaccess = @unannotatedtypeaccess | @annotatedtypeaccess + +@assignment = @assignexpr + | @assignop; + +@unaryassignment = @postincexpr + | @postdecexpr + | @preincexpr + | @predecexpr; + +@assignop = @assignaddexpr + | @assignsubexpr + | @assignmulexpr + | @assigndivexpr + | @assignremexpr + | @assignandexpr + | @assignorexpr + | @assignxorexpr + | @assignlshiftexpr + | @assignrshiftexpr + | @assignurshiftexpr; + +@literal = @booleanliteral + | @integerliteral + | @longliteral + | @floatingpointliteral + | @doubleliteral + | @characterliteral + | @stringliteral + | @nullliteral; + +@binaryexpr = @mulexpr + | @divexpr + | @remexpr + | @addexpr + | @subexpr + | @lshiftexpr + | @rshiftexpr + | @urshiftexpr + | @andbitexpr + | @orbitexpr + | @xorbitexpr + | @andlogicalexpr + | @orlogicalexpr + | @ltexpr + | @gtexpr + | @leexpr + | @geexpr + | @eqexpr + | @neexpr + | @valueeqexpr + | @valueneexpr; + +@unaryexpr = @postincexpr + | @postdecexpr + | @preincexpr + | @predecexpr + | @minusexpr + | @plusexpr + | @bitnotexpr + | @lognotexpr + | @notnullexpr; + +@caller = @classinstancexpr + | @methodaccess + | @constructorinvocationstmt + | @superconstructorinvocationstmt; + +callableBinding( + unique int callerid: @caller ref, + int callee: @callable ref +); + +memberRefBinding( + unique int id: @expr ref, + int callable: @callable ref +); + +propertyRefGetBinding( + unique int id: @expr ref, + int getter: @callable ref +); + +propertyRefFieldBinding( + unique int id: @expr ref, + int field: @field ref +); + +propertyRefSetBinding( + unique int id: @expr ref, + int setter: @callable ref +); + +@exprparent = @stmt | @expr | @whenbranch | @callable | @field | @fielddecl | @class | @interface | @param | @localvar | @typevariable; + +variableBinding( + unique int expr: @varaccess ref, + int variable: @variable ref +); + +@variable = @localscopevariable | @field; + +@localscopevariable = @localvar | @param; + +localvars( + unique int id: @localvar, + string nodeName: string ref, + int typeid: @type ref, + int parentid: @localvariabledeclexpr ref +); + +localvarsKotlinType( + unique int id: @localvar ref, + int kttypeid: @kt_type ref +); + +@namedexprorstmt = @breakstmt + | @continuestmt + | @labeledstmt + | @literal; + +namestrings( + string name: string ref, + string value: string ref, + unique int parent: @namedexprorstmt ref +); + +/* + * Modules + */ + +#keyset[name] +modules( + unique int id: @module, + string name: string ref +); + +isOpen( + int id: @module ref +); + +#keyset[fileId] +cumodule( + int fileId: @file ref, + int moduleId: @module ref +); + +@directive = @requires + | @exports + | @opens + | @uses + | @provides + +#keyset[directive] +directives( + int id: @module ref, + int directive: @directive ref +); + +requires( + unique int id: @requires, + int target: @module ref +); + +isTransitive( + int id: @requires ref +); + +isStatic( + int id: @requires ref +); + +exports( + unique int id: @exports, + int target: @package ref +); + +exportsTo( + int id: @exports ref, + int target: @module ref +); + +opens( + unique int id: @opens, + int target: @package ref +); + +opensTo( + int id: @opens ref, + int target: @module ref +); + +uses( + unique int id: @uses, + string serviceInterface: string ref +); + +provides( + unique int id: @provides, + string serviceInterface: string ref +); + +providesWith( + int id: @provides ref, + string serviceImpl: string ref +); + +/* + * Javadoc + */ + +javadoc( + unique int id: @javadoc +); + +isNormalComment( + int commentid : @javadoc ref +); + +isEolComment( + int commentid : @javadoc ref +); + +hasJavadoc( + int documentableid: @member ref, + int javadocid: @javadoc ref +); + +#keyset[parentid,idx] +javadocTag( + unique int id: @javadocTag, + string name: string ref, + int parentid: @javadocParent ref, + int idx: int ref +); + +#keyset[parentid,idx] +javadocText( + unique int id: @javadocText, + string text: string ref, + int parentid: @javadocParent ref, + int idx: int ref +); + +@javadocParent = @javadoc | @javadocTag; +@javadocElement = @javadocTag | @javadocText; + +@classorinterface = @interface | @class; +@classorinterfaceorpackage = @classorinterface | @package; +@classorinterfaceorcallable = @classorinterface | @callable; +@boundedtype = @typevariable | @wildcard; +@reftype = @classorinterface | @array | @boundedtype; +@classorarray = @class | @array; +@type = @primitive | @reftype; +@callable = @method | @constructor; + +/** A program element that has a name. */ +@element = @package | @modifier | @annotation | + @locatableElement; + +@locatableElement = @file | @primitive | @class | @interface | @method | @constructor | @param | @exception | @field | + @boundedtype | @array | @localvar | @expr | @stmt | @import | @fielddecl | @kt_type | @kt_type_alias | + @kt_property; + +@modifiable = @member_modifiable| @param | @localvar ; + +@member_modifiable = @class | @interface | @method | @constructor | @field | @kt_property; + +@member = @method | @constructor | @field | @reftype ; + +/** A program element that has a location. */ +@locatable = @typebound | @javadoc | @javadocTag | @javadocText | @xmllocatable | @ktcomment | + @locatableElement; + +@top = @element | @locatable | @folder; + +/* + * XML Files + */ + +xmlEncoding( + unique int id: @file ref, + string encoding: string ref +); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; + +/* + * configuration files with key value pairs + */ + +configs( + unique int id: @config +); + +configNames( + unique int id: @configName, + int config: @config ref, + string name: string ref +); + +configValues( + unique int id: @configValue, + int config: @config ref, + string value: string ref +); + +configLocations( + int locatable: @configLocatable ref, + int location: @location_default ref +); + +@configLocatable = @config | @configName | @configValue; + +ktComments( + unique int id: @ktcomment, + int kind: int ref, + string text : string ref +) + +ktCommentSections( + unique int id: @ktcommentsection, + int comment: @ktcomment ref, + string content : string ref +) + +ktCommentSectionNames( + unique int id: @ktcommentsection ref, + string name : string ref +) + +ktCommentSectionSubjectNames( + unique int id: @ktcommentsection ref, + string subjectname : string ref +) + +#keyset[id, owner] +ktCommentOwners( + int id: @ktcomment ref, + int owner: @top ref +) + +ktExtensionFunctions( + unique int id: @method ref, + int typeid: @type ref, + int kttypeid: @kt_type ref +) + +ktProperties( + unique int id: @kt_property, + string nodeName: string ref +) + +ktPropertyGetters( + unique int id: @kt_property ref, + int getter: @method ref +) + +ktPropertySetters( + unique int id: @kt_property ref, + int setter: @method ref +) + +ktPropertyBackingFields( + unique int id: @kt_property ref, + int backingField: @field ref +) + +ktSyntheticBody( + unique int id: @callable ref, + int kind: int ref + // 1: ENUM_VALUES + // 2: ENUM_VALUEOF +) + +ktLocalFunction( + unique int id: @method ref +) + +ktInitializerAssignment( + unique int id: @assignexpr ref +) + +ktPropertyDelegates( + unique int id: @kt_property ref, + unique int variableId: @variable ref +) + +/** + * If `id` is a compiler generated element, then the kind indicates the + * reason that the compiler generated it. + * See `Element.compilerGeneratedReason()` for an explanation of what + * each `kind` means. + */ +compiler_generated( + unique int id: @element ref, + int kind: int ref +) + +ktFunctionOriginalNames( + unique int id: @method ref, + string name: string ref +) diff --git a/java/ql/lib/upgrades/cf58c7d9b1fa1eae9cdc20ce8f157c140ac0c3de/semmlecode.dbscheme b/java/ql/lib/upgrades/cf58c7d9b1fa1eae9cdc20ce8f157c140ac0c3de/semmlecode.dbscheme new file mode 100755 index 00000000000..81ccfabe82e --- /dev/null +++ b/java/ql/lib/upgrades/cf58c7d9b1fa1eae9cdc20ce8f157c140ac0c3de/semmlecode.dbscheme @@ -0,0 +1,1236 @@ +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * javac A.java B.java C.java + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + /** + * An invocation of the compiler. Note that more than one file may + * be compiled per invocation. For example, this command compiles + * three source files: + * + * javac A.java B.java C.java + */ + unique int id : @compilation, + int kind: int ref, + string cwd : string ref, + string name : string ref +); + +case @compilation.kind of + 1 = @javacompilation +| 2 = @kotlincompilation +; + +compilation_started( + int id : @compilation ref +) + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * javac A.java B.java C.java + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | *path to extractor* + * 1 | `--javac-args` + * 2 | A.java + * 3 | B.java + * 4 | C.java + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * javac A.java B.java C.java + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | A.java + * 1 | B.java + * 2 | C.java + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * For each file recorded in `compilation_compiling_files`, + * there will be a corresponding row in + * `compilation_compiling_files_completed` once extraction + * of that file is complete. The `result` will indicate the + * extraction result: + * + * 0: Successfully extracted + * 1: Errors were encountered, but extraction recovered + * 2: Errors were encountered, and extraction could not recover + */ +#keyset[id, num] +compilation_compiling_files_completed( + int id : @compilation ref, + int num : int ref, + int result : int ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + unique int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +/** + * The `cpu_seconds` and `elapsed_seconds` are the CPU time and elapsed + * time (respectively) that the original compilation (not the extraction) + * took for compiler invocation `id`. + */ +compilation_compiler_times( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + * The `result` will indicate the extraction result: + * + * 0: Successfully extracted + * 1: Errors were encountered, but extraction recovered + * 2: Errors were encountered, and extraction could not recover + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref, + int result : int ref +); + +diagnostics( + unique int id: @diagnostic, + string generated_by: string ref, // TODO: Sync this with the other languages? + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +/* + * External artifacts + */ + +externalData( + int id : @externalDataElement, + string path : string ref, + int column: int ref, + string value : string ref +); + +snapshotDate( + unique date snapshotDate : date ref +); + +sourceLocationPrefix( + string prefix : string ref +); + +/* + * Duplicate code + */ + +duplicateCode( + unique int id : @duplication, + string relativePath : string ref, + int equivClass : int ref +); + +similarCode( + unique int id : @similarity, + string relativePath : string ref, + int equivClass : int ref +); + +@duplication_or_similarity = @duplication | @similarity + +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref +); + +/* + * SMAP + */ + +smap_header( + int outputFileId: @file ref, + string outputFilename: string ref, + string defaultStratum: string ref +); + +smap_files( + int outputFileId: @file ref, + string stratum: string ref, + int inputFileNum: int ref, + string inputFileName: string ref, + int inputFileId: @file ref +); + +smap_lines( + int outputFileId: @file ref, + string stratum: string ref, + int inputFileNum: int ref, + int inputStartLine: int ref, + int inputLineCount: int ref, + int outputStartLine: int ref, + int outputLineIncrement: int ref +); + +/* + * Locations and files + */ + +@location = @location_default ; + +locations_default( + unique int id: @location_default, + int file: @file ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +hasLocation( + int locatableid: @locatable ref, + int id: @location ref +); + +@sourceline = @locatable ; + +#keyset[element_id] +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +files( + unique int id: @file, + string name: string ref +); + +folders( + unique int id: @folder, + string name: string ref +); + +@container = @folder | @file + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +/* + * Java + */ + +cupackage( + unique int id: @file ref, + int packageid: @package ref +); + +#keyset[fileid,keyName] +jarManifestMain( + int fileid: @file ref, + string keyName: string ref, + string value: string ref +); + +#keyset[fileid,entryName,keyName] +jarManifestEntries( + int fileid: @file ref, + string entryName: string ref, + string keyName: string ref, + string value: string ref +); + +packages( + unique int id: @package, + string nodeName: string ref +); + +primitives( + unique int id: @primitive, + string nodeName: string ref +); + +modifiers( + unique int id: @modifier, + string nodeName: string ref +); + +/** + * An errortype is used when the extractor is unable to extract a type + * correctly for some reason. + */ +error_type( + unique int id: @errortype +); + +classes( + unique int id: @class, + string nodeName: string ref, + int parentid: @package ref, + int sourceid: @class ref +); + +file_class( + int id: @class ref +); + +class_object( + unique int id: @class ref, + unique int instance: @field ref +); + +type_companion_object( + unique int id: @classorinterface ref, + unique int instance: @field ref, + unique int companion_object: @class ref +); + +kt_nullable_types( + unique int id: @kt_nullable_type, + int classid: @reftype ref +) + +kt_notnull_types( + unique int id: @kt_notnull_type, + int classid: @reftype ref +) + +kt_type_alias( + unique int id: @kt_type_alias, + string name: string ref, + int kttypeid: @kt_type ref +) + +@kt_type = @kt_nullable_type | @kt_notnull_type + +isRecord( + unique int id: @class ref +); + +interfaces( + unique int id: @interface, + string nodeName: string ref, + int parentid: @package ref, + int sourceid: @interface ref +); + +fielddecls( + unique int id: @fielddecl, + int parentid: @reftype ref +); + +#keyset[fieldId] #keyset[fieldDeclId,pos] +fieldDeclaredIn( + int fieldId: @field ref, + int fieldDeclId: @fielddecl ref, + int pos: int ref +); + +fields( + unique int id: @field, + string nodeName: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @field ref +); + +fieldsKotlinType( + unique int id: @field ref, + int kttypeid: @kt_type ref +); + +constrs( + unique int id: @constructor, + string nodeName: string ref, + string signature: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @constructor ref +); + +constrsKotlinType( + unique int id: @constructor ref, + int kttypeid: @kt_type ref +); + +methods( + unique int id: @method, + string nodeName: string ref, + string signature: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @method ref +); + +methodsKotlinType( + unique int id: @method ref, + int kttypeid: @kt_type ref +); + +#keyset[parentid,pos] +params( + unique int id: @param, + int typeid: @type ref, + int pos: int ref, + int parentid: @callable ref, + int sourceid: @param ref +); + +paramsKotlinType( + unique int id: @param ref, + int kttypeid: @kt_type ref +); + +paramName( + unique int id: @param ref, + string nodeName: string ref +); + +isVarargsParam( + int param: @param ref +); + +exceptions( + unique int id: @exception, + int typeid: @type ref, + int parentid: @callable ref +); + +isAnnotType( + int interfaceid: @interface ref +); + +isAnnotElem( + int methodid: @method ref +); + +annotValue( + int parentid: @annotation ref, + int id2: @method ref, + unique int value: @expr ref +); + +isEnumType( + int classid: @class ref +); + +isEnumConst( + int fieldid: @field ref +); + +#keyset[parentid,pos] +typeVars( + unique int id: @typevariable, + string nodeName: string ref, + int pos: int ref, + int kind: int ref, // deprecated + int parentid: @classorinterfaceorcallable ref +); + +wildcards( + unique int id: @wildcard, + string nodeName: string ref, + int kind: int ref +); + +#keyset[parentid,pos] +typeBounds( + unique int id: @typebound, + int typeid: @reftype ref, + int pos: int ref, + int parentid: @boundedtype ref +); + +#keyset[parentid,pos] +typeArgs( + int argumentid: @reftype ref, + int pos: int ref, + int parentid: @classorinterfaceorcallable ref +); + +isParameterized( + int memberid: @member ref +); + +isRaw( + int memberid: @member ref +); + +erasure( + unique int memberid: @member ref, + int erasureid: @member ref +); + +#keyset[classid] #keyset[parent] +isAnonymClass( + int classid: @class ref, + int parent: @classinstancexpr ref +); + +#keyset[typeid] #keyset[parent] +isLocalClassOrInterface( + int typeid: @classorinterface ref, + int parent: @localtypedeclstmt ref +); + +isDefConstr( + int constructorid: @constructor ref +); + +#keyset[exprId] +lambdaKind( + int exprId: @lambdaexpr ref, + int bodyKind: int ref +); + +arrays( + unique int id: @array, + string nodeName: string ref, + int elementtypeid: @type ref, + int dimension: int ref, + int componenttypeid: @type ref +); + +enclInReftype( + unique int child: @reftype ref, + int parent: @reftype ref +); + +extendsReftype( + int id1: @reftype ref, + int id2: @classorinterface ref +); + +implInterface( + int id1: @classorarray ref, + int id2: @interface ref +); + +permits( + int id1: @classorinterface ref, + int id2: @classorinterface ref +); + +hasModifier( + int id1: @modifiable ref, + int id2: @modifier ref +); + +imports( + unique int id: @import, + int holder: @classorinterfaceorpackage ref, + string name: string ref, + int kind: int ref +); + +#keyset[parent,idx] +stmts( + unique int id: @stmt, + int kind: int ref, + int parent: @stmtparent ref, + int idx: int ref, + int bodydecl: @callable ref +); + +@stmtparent = @callable | @stmt | @switchexpr | @whenexpr| @stmtexpr; + +case @stmt.kind of + 0 = @block +| 1 = @ifstmt +| 2 = @forstmt +| 3 = @enhancedforstmt +| 4 = @whilestmt +| 5 = @dostmt +| 6 = @trystmt +| 7 = @switchstmt +| 8 = @synchronizedstmt +| 9 = @returnstmt +| 10 = @throwstmt +| 11 = @breakstmt +| 12 = @continuestmt +| 13 = @emptystmt +| 14 = @exprstmt +| 15 = @labeledstmt +| 16 = @assertstmt +| 17 = @localvariabledeclstmt +| 18 = @localtypedeclstmt +| 19 = @constructorinvocationstmt +| 20 = @superconstructorinvocationstmt +| 21 = @case +| 22 = @catchclause +| 23 = @yieldstmt +| 24 = @errorstmt +| 25 = @whenbranch +; + +#keyset[parent,idx] +exprs( + unique int id: @expr, + int kind: int ref, + int typeid: @type ref, + int parent: @exprparent ref, + int idx: int ref +); + +exprsKotlinType( + unique int id: @expr ref, + int kttypeid: @kt_type ref +); + +callableEnclosingExpr( + unique int id: @expr ref, + int callable_id: @callable ref +); + +statementEnclosingExpr( + unique int id: @expr ref, + int statement_id: @stmt ref +); + +isParenthesized( + unique int id: @expr ref, + int parentheses: int ref +); + +case @expr.kind of + 1 = @arrayaccess +| 2 = @arraycreationexpr +| 3 = @arrayinit +| 4 = @assignexpr +| 5 = @assignaddexpr +| 6 = @assignsubexpr +| 7 = @assignmulexpr +| 8 = @assigndivexpr +| 9 = @assignremexpr +| 10 = @assignandexpr +| 11 = @assignorexpr +| 12 = @assignxorexpr +| 13 = @assignlshiftexpr +| 14 = @assignrshiftexpr +| 15 = @assignurshiftexpr +| 16 = @booleanliteral +| 17 = @integerliteral +| 18 = @longliteral +| 19 = @floatingpointliteral +| 20 = @doubleliteral +| 21 = @characterliteral +| 22 = @stringliteral +| 23 = @nullliteral +| 24 = @mulexpr +| 25 = @divexpr +| 26 = @remexpr +| 27 = @addexpr +| 28 = @subexpr +| 29 = @lshiftexpr +| 30 = @rshiftexpr +| 31 = @urshiftexpr +| 32 = @andbitexpr +| 33 = @orbitexpr +| 34 = @xorbitexpr +| 35 = @andlogicalexpr +| 36 = @orlogicalexpr +| 37 = @ltexpr +| 38 = @gtexpr +| 39 = @leexpr +| 40 = @geexpr +| 41 = @eqexpr +| 42 = @neexpr +| 43 = @postincexpr +| 44 = @postdecexpr +| 45 = @preincexpr +| 46 = @predecexpr +| 47 = @minusexpr +| 48 = @plusexpr +| 49 = @bitnotexpr +| 50 = @lognotexpr +| 51 = @castexpr +| 52 = @newexpr +| 53 = @conditionalexpr +| 54 = @parexpr // deprecated +| 55 = @instanceofexpr +| 56 = @localvariabledeclexpr +| 57 = @typeliteral +| 58 = @thisaccess +| 59 = @superaccess +| 60 = @varaccess +| 61 = @methodaccess +| 62 = @unannotatedtypeaccess +| 63 = @arraytypeaccess +| 64 = @packageaccess +| 65 = @wildcardtypeaccess +| 66 = @declannotation +| 67 = @uniontypeaccess +| 68 = @lambdaexpr +| 69 = @memberref +| 70 = @annotatedtypeaccess +| 71 = @typeannotation +| 72 = @intersectiontypeaccess +| 73 = @switchexpr +| 74 = @errorexpr +| 75 = @whenexpr +| 76 = @getclassexpr +| 77 = @safecastexpr +| 78 = @implicitcastexpr +| 79 = @implicitnotnullexpr +| 80 = @implicitcoerciontounitexpr +| 81 = @notinstanceofexpr +| 82 = @stmtexpr +| 83 = @stringtemplateexpr +| 84 = @notnullexpr +| 85 = @unsafecoerceexpr +| 86 = @valueeqexpr +| 87 = @valueneexpr +| 88 = @propertyref +; + +/** Holds if this `when` expression was written as an `if` expression. */ +when_if(unique int id: @whenexpr ref); + +/** Holds if this `when` branch was written as an `else` branch. */ +when_branch_else(unique int id: @whenbranch ref); + +@classinstancexpr = @newexpr | @lambdaexpr | @memberref | @propertyref + +@annotation = @declannotation | @typeannotation +@typeaccess = @unannotatedtypeaccess | @annotatedtypeaccess + +@assignment = @assignexpr + | @assignop; + +@unaryassignment = @postincexpr + | @postdecexpr + | @preincexpr + | @predecexpr; + +@assignop = @assignaddexpr + | @assignsubexpr + | @assignmulexpr + | @assigndivexpr + | @assignremexpr + | @assignandexpr + | @assignorexpr + | @assignxorexpr + | @assignlshiftexpr + | @assignrshiftexpr + | @assignurshiftexpr; + +@literal = @booleanliteral + | @integerliteral + | @longliteral + | @floatingpointliteral + | @doubleliteral + | @characterliteral + | @stringliteral + | @nullliteral; + +@binaryexpr = @mulexpr + | @divexpr + | @remexpr + | @addexpr + | @subexpr + | @lshiftexpr + | @rshiftexpr + | @urshiftexpr + | @andbitexpr + | @orbitexpr + | @xorbitexpr + | @andlogicalexpr + | @orlogicalexpr + | @ltexpr + | @gtexpr + | @leexpr + | @geexpr + | @eqexpr + | @neexpr + | @valueeqexpr + | @valueneexpr; + +@unaryexpr = @postincexpr + | @postdecexpr + | @preincexpr + | @predecexpr + | @minusexpr + | @plusexpr + | @bitnotexpr + | @lognotexpr + | @notnullexpr; + +@caller = @classinstancexpr + | @methodaccess + | @constructorinvocationstmt + | @superconstructorinvocationstmt; + +callableBinding( + unique int callerid: @caller ref, + int callee: @callable ref +); + +memberRefBinding( + unique int id: @expr ref, + int callable: @callable ref +); + +propertyRefGetBinding( + unique int id: @expr ref, + int getter: @callable ref +); + +propertyRefFieldBinding( + unique int id: @expr ref, + int field: @field ref +); + +propertyRefSetBinding( + unique int id: @expr ref, + int setter: @callable ref +); + +@exprparent = @stmt | @expr | @whenbranch | @callable | @field | @fielddecl | @class | @interface | @param | @localvar | @typevariable; + +variableBinding( + unique int expr: @varaccess ref, + int variable: @variable ref +); + +@variable = @localscopevariable | @field; + +@localscopevariable = @localvar | @param; + +localvars( + unique int id: @localvar, + string nodeName: string ref, + int typeid: @type ref, + int parentid: @localvariabledeclexpr ref +); + +localvarsKotlinType( + unique int id: @localvar ref, + int kttypeid: @kt_type ref +); + +@namedexprorstmt = @breakstmt + | @continuestmt + | @labeledstmt + | @literal; + +namestrings( + string name: string ref, + string value: string ref, + unique int parent: @namedexprorstmt ref +); + +/* + * Modules + */ + +#keyset[name] +modules( + unique int id: @module, + string name: string ref +); + +isOpen( + int id: @module ref +); + +#keyset[fileId] +cumodule( + int fileId: @file ref, + int moduleId: @module ref +); + +@directive = @requires + | @exports + | @opens + | @uses + | @provides + +#keyset[directive] +directives( + int id: @module ref, + int directive: @directive ref +); + +requires( + unique int id: @requires, + int target: @module ref +); + +isTransitive( + int id: @requires ref +); + +isStatic( + int id: @requires ref +); + +exports( + unique int id: @exports, + int target: @package ref +); + +exportsTo( + int id: @exports ref, + int target: @module ref +); + +opens( + unique int id: @opens, + int target: @package ref +); + +opensTo( + int id: @opens ref, + int target: @module ref +); + +uses( + unique int id: @uses, + string serviceInterface: string ref +); + +provides( + unique int id: @provides, + string serviceInterface: string ref +); + +providesWith( + int id: @provides ref, + string serviceImpl: string ref +); + +/* + * Javadoc + */ + +javadoc( + unique int id: @javadoc +); + +isNormalComment( + int commentid : @javadoc ref +); + +isEolComment( + int commentid : @javadoc ref +); + +hasJavadoc( + int documentableid: @member ref, + int javadocid: @javadoc ref +); + +#keyset[parentid,idx] +javadocTag( + unique int id: @javadocTag, + string name: string ref, + int parentid: @javadocParent ref, + int idx: int ref +); + +#keyset[parentid,idx] +javadocText( + unique int id: @javadocText, + string text: string ref, + int parentid: @javadocParent ref, + int idx: int ref +); + +@javadocParent = @javadoc | @javadocTag; +@javadocElement = @javadocTag | @javadocText; + +@classorinterface = @interface | @class; +@classorinterfaceorpackage = @classorinterface | @package; +@classorinterfaceorcallable = @classorinterface | @callable; +@boundedtype = @typevariable | @wildcard; +@reftype = @classorinterface | @array | @boundedtype | @errortype; +@classorarray = @class | @array; +@type = @primitive | @reftype; +@callable = @method | @constructor; + +/** A program element that has a name. */ +@element = @package | @modifier | @annotation | @errortype | + @locatableElement; + +@locatableElement = @file | @primitive | @class | @interface | @method | @constructor | @param | @exception | @field | + @boundedtype | @array | @localvar | @expr | @stmt | @import | @fielddecl | @kt_type | @kt_type_alias | + @kt_property; + +@modifiable = @member_modifiable| @param | @localvar ; + +@member_modifiable = @class | @interface | @method | @constructor | @field | @kt_property; + +@member = @method | @constructor | @field | @reftype ; + +/** A program element that has a location. */ +@locatable = @typebound | @javadoc | @javadocTag | @javadocText | @xmllocatable | @ktcomment | + @locatableElement; + +@top = @element | @locatable | @folder; + +/* + * XML Files + */ + +xmlEncoding( + unique int id: @file ref, + string encoding: string ref +); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; + +/* + * configuration files with key value pairs + */ + +configs( + unique int id: @config +); + +configNames( + unique int id: @configName, + int config: @config ref, + string name: string ref +); + +configValues( + unique int id: @configValue, + int config: @config ref, + string value: string ref +); + +configLocations( + int locatable: @configLocatable ref, + int location: @location_default ref +); + +@configLocatable = @config | @configName | @configValue; + +ktComments( + unique int id: @ktcomment, + int kind: int ref, + string text : string ref +) + +ktCommentSections( + unique int id: @ktcommentsection, + int comment: @ktcomment ref, + string content : string ref +) + +ktCommentSectionNames( + unique int id: @ktcommentsection ref, + string name : string ref +) + +ktCommentSectionSubjectNames( + unique int id: @ktcommentsection ref, + string subjectname : string ref +) + +#keyset[id, owner] +ktCommentOwners( + int id: @ktcomment ref, + int owner: @top ref +) + +ktExtensionFunctions( + unique int id: @method ref, + int typeid: @type ref, + int kttypeid: @kt_type ref +) + +ktProperties( + unique int id: @kt_property, + string nodeName: string ref +) + +ktPropertyGetters( + unique int id: @kt_property ref, + int getter: @method ref +) + +ktPropertySetters( + unique int id: @kt_property ref, + int setter: @method ref +) + +ktPropertyBackingFields( + unique int id: @kt_property ref, + int backingField: @field ref +) + +ktSyntheticBody( + unique int id: @callable ref, + int kind: int ref + // 1: ENUM_VALUES + // 2: ENUM_VALUEOF +) + +ktLocalFunction( + unique int id: @method ref +) + +ktInitializerAssignment( + unique int id: @assignexpr ref +) + +ktPropertyDelegates( + unique int id: @kt_property ref, + unique int variableId: @variable ref +) + +/** + * If `id` is a compiler generated element, then the kind indicates the + * reason that the compiler generated it. + * See `Element.compilerGeneratedReason()` for an explanation of what + * each `kind` means. + */ +compiler_generated( + unique int id: @element ref, + int kind: int ref +) + +ktFunctionOriginalNames( + unique int id: @method ref, + string name: string ref +) diff --git a/java/ql/lib/upgrades/cf58c7d9b1fa1eae9cdc20ce8f157c140ac0c3de/upgrade.properties b/java/ql/lib/upgrades/cf58c7d9b1fa1eae9cdc20ce8f157c140ac0c3de/upgrade.properties new file mode 100644 index 00000000000..25c651f591b --- /dev/null +++ b/java/ql/lib/upgrades/cf58c7d9b1fa1eae9cdc20ce8f157c140ac0c3de/upgrade.properties @@ -0,0 +1,2 @@ +description: Add errortype +compatibility: full From 39406436bfa54a3ee3581573f4e4f1928c7a6c80 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Mon, 11 Jul 2022 15:11:13 +0200 Subject: [PATCH 167/505] Swift: extract `IfConfigDecl` This also adds `UnresolvedDeclRefExpr` tests, as `IfConfigDecl` consistently introduces those. --- swift/codegen/schema.yml | 11 ++++++- swift/extractor/infra/SwiftDispatcher.h | 19 ++++++++++-- swift/extractor/infra/SwiftTagTraits.h | 1 + swift/extractor/visitors/DeclVisitor.cpp | 14 +++++++++ swift/extractor/visitors/DeclVisitor.h | 7 +++++ swift/extractor/visitors/SwiftVisitor.h | 1 + swift/ql/lib/codeql/swift/elements.qll | 1 + .../swift/elements/decl/IfConfigClause.qll | 4 +++ .../elements/expr/UnresolvedDeclRefExpr.qll | 9 ++++-- .../swift/generated/GetImmediateParent.qll | 6 ++++ .../swift/generated/decl/IfConfigClause.qll | 30 +++++++++++++++++++ .../swift/generated/decl/IfConfigDecl.qll | 12 ++++++++ swift/ql/lib/swift.dbscheme | 30 +++++++++++++++++++ .../IfConfigClause/IfConfigClause.expected | 6 ++++ .../decl/IfConfigClause/IfConfigClause.ql | 10 +++++++ .../IfConfigClause_getCondition.expected | 4 +++ .../IfConfigClause_getCondition.ql | 7 +++++ .../IfConfigClause_getElement.expected | 18 +++++++++++ .../IfConfigClause_getElement.ql | 7 +++++ .../decl/IfConfigClause/if_config.swift | 10 +++++++ .../IfConfigClause/if_config_active.swift | 12 ++++++++ .../decl/IfConfigDecl/IfConfigDecl.expected | 1 + .../decl/IfConfigDecl/IfConfigDecl.ql | 7 +++++ .../IfConfigDecl_getClause.expected | 3 ++ .../IfConfigDecl/IfConfigDecl_getClause.ql | 7 +++++ .../decl/IfConfigDecl/MISSING_SOURCE.txt | 4 --- .../decl/IfConfigDecl/if_config.swift | 10 +++++++ .../UnresolvedDeclRefExpr.expected | 10 +++++++ .../UnresolvedDeclRefExpr.ql | 7 +++++ .../UnresolvedDeclRefExpr_getName.expected | 10 +++++++ .../UnresolvedDeclRefExpr_getName.ql | 7 +++++ .../UnresolvedDeclRefExpr_getType.expected | 0 .../UnresolvedDeclRefExpr_getType.ql | 7 +++++ .../unresolved_decl_ref.swift | 10 +++++++ 34 files changed, 293 insertions(+), 9 deletions(-) create mode 100644 swift/ql/lib/codeql/swift/elements/decl/IfConfigClause.qll create mode 100644 swift/ql/lib/codeql/swift/generated/decl/IfConfigClause.qll create mode 100644 swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause.expected create mode 100644 swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause.ql create mode 100644 swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause_getCondition.expected create mode 100644 swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause_getCondition.ql create mode 100644 swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause_getElement.expected create mode 100644 swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause_getElement.ql create mode 100644 swift/ql/test/extractor-tests/generated/decl/IfConfigClause/if_config.swift create mode 100644 swift/ql/test/extractor-tests/generated/decl/IfConfigClause/if_config_active.swift create mode 100644 swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/IfConfigDecl.expected create mode 100644 swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/IfConfigDecl.ql create mode 100644 swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/IfConfigDecl_getClause.expected create mode 100644 swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/IfConfigDecl_getClause.ql delete mode 100644 swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/MISSING_SOURCE.txt create mode 100644 swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/if_config.swift create mode 100644 swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/UnresolvedDeclRefExpr.expected create mode 100644 swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/UnresolvedDeclRefExpr.ql create mode 100644 swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/UnresolvedDeclRefExpr_getName.expected create mode 100644 swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/UnresolvedDeclRefExpr_getName.ql create mode 100644 swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/UnresolvedDeclRefExpr_getType.expected create mode 100644 swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/UnresolvedDeclRefExpr_getType.ql create mode 100644 swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/unresolved_decl_ref.swift diff --git a/swift/codegen/schema.yml b/swift/codegen/schema.yml index 96da8c96936..540f187d021 100644 --- a/swift/codegen/schema.yml +++ b/swift/codegen/schema.yml @@ -270,6 +270,16 @@ EnumCaseDecl: IfConfigDecl: _extends: Decl + _children: + clauses: IfConfigClause* + +IfConfigClause: + _extends: Locatable + _children: + condition: Expr? + elements: AstNode* + is_active: predicate + _dir: decl ImportDecl: _extends: Decl @@ -545,7 +555,6 @@ TypeExpr: UnresolvedDeclRefExpr: _extends: Expr name: string? - _pragma: qltest_skip # we should really never extract these UnresolvedDotExpr: _extends: Expr diff --git a/swift/extractor/infra/SwiftDispatcher.h b/swift/extractor/infra/SwiftDispatcher.h index c7ca43a1614..095ddf0a7aa 100644 --- a/swift/extractor/infra/SwiftDispatcher.h +++ b/swift/extractor/infra/SwiftDispatcher.h @@ -78,7 +78,7 @@ class SwiftDispatcher { waitingForNewLabel = e; visit(e); if (auto l = store.get(e)) { - if constexpr (!std::is_base_of_v) { + if constexpr (std::is_base_of_v>) { attachLocation(e, *l); } return *l; @@ -95,6 +95,10 @@ class SwiftDispatcher { return fetchLabelFromUnion(node); } + TrapLabel fetchLabel(const swift::IfConfigClause& clause) { + return fetchLabel(&clause); + } + // Due to the lazy emission approach, we must assign a label to a corresponding AST node before // it actually gets emitted to handle recursive cases such as recursive calls, or recursive type // declarations @@ -143,6 +147,15 @@ class SwiftDispatcher { attachLocation(locatable->getStartLoc(), locatable->getEndLoc(), locatableLabel); } + void attachLocation(const swift::IfConfigClause* clause, TrapLabel locatableLabel) { + attachLocation(clause->Loc, clause->Loc, locatableLabel); + } + + // Emits a Location TRAP entry and attaches it to a `Locatable` trap label for a given `SourceLoc` + void attachLocation(swift::SourceLoc loc, TrapLabel locatableLabel) { + attachLocation(loc, loc, locatableLabel); + } + // Emits a Location TRAP entry for a list of swift entities and attaches it to a `Locatable` trap // label template @@ -217,7 +230,8 @@ class SwiftDispatcher { swift::Expr, swift::Pattern, swift::TypeRepr, - swift::TypeBase>; + swift::TypeBase, + swift::IfConfigClause>; void attachLocation(swift::SourceLoc start, swift::SourceLoc end, @@ -274,6 +288,7 @@ class SwiftDispatcher { // TODO: The following methods are supposed to redirect TRAP emission to correpsonding visitors, // which are to be introduced in follow-up PRs virtual void visit(swift::Decl* decl) = 0; + virtual void visit(const swift::IfConfigClause* clause) = 0; virtual void visit(swift::Stmt* stmt) = 0; virtual void visit(swift::StmtCondition* cond) = 0; virtual void visit(swift::CaseLabelItem* item) = 0; diff --git a/swift/extractor/infra/SwiftTagTraits.h b/swift/extractor/infra/SwiftTagTraits.h index 8a2e489683d..1de7312df50 100644 --- a/swift/extractor/infra/SwiftTagTraits.h +++ b/swift/extractor/infra/SwiftTagTraits.h @@ -47,6 +47,7 @@ MAP_TAG(Expr); #include MAP_TAG(Decl); +MAP_TAG(IfConfigClause); #define ABSTRACT_DECL(CLASS, PARENT) MAP_SUBTAG(CLASS##Decl, PARENT) #define DECL(CLASS, PARENT) ABSTRACT_DECL(CLASS, PARENT) #include diff --git a/swift/extractor/visitors/DeclVisitor.cpp b/swift/extractor/visitors/DeclVisitor.cpp index 33f746f6634..819c959a6d2 100644 --- a/swift/extractor/visitors/DeclVisitor.cpp +++ b/swift/extractor/visitors/DeclVisitor.cpp @@ -358,4 +358,18 @@ void DeclVisitor::fillAbstractStorageDecl(const swift::AbstractStorageDecl& decl fillValueDecl(decl, entry); } +codeql::IfConfigDecl DeclVisitor::translateIfConfigDecl(const swift::IfConfigDecl& decl) { + auto entry = dispatcher_.createEntry(decl); + entry.clauses = dispatcher_.fetchRepeatedLabels(decl.getClauses()); + return entry; +} + +codeql::IfConfigClause DeclVisitor::translateIfConfigClause(const swift::IfConfigClause& clause) { + auto entry = dispatcher_.createEntry(clause); + entry.condition = dispatcher_.fetchOptionalLabel(clause.Cond); + entry.elements = dispatcher_.fetchRepeatedLabels(clause.Elements); + entry.is_active = clause.isActive; + return entry; +} + } // namespace codeql diff --git a/swift/extractor/visitors/DeclVisitor.h b/swift/extractor/visitors/DeclVisitor.h index c9a1b43556d..0d6296591a0 100644 --- a/swift/extractor/visitors/DeclVisitor.h +++ b/swift/extractor/visitors/DeclVisitor.h @@ -14,6 +14,11 @@ namespace codeql { class DeclVisitor : public AstVisitorBase { public: using AstVisitorBase::AstVisitorBase; + using AstVisitorBase::visit; + + void visit(const swift::IfConfigClause* clause) { + dispatcher_.emit(translateIfConfigClause(*clause)); + } std::variant translateFuncDecl( const swift::FuncDecl& decl); @@ -52,6 +57,8 @@ class DeclVisitor : public AstVisitorBase { codeql::ExtensionDecl translateExtensionDecl(const swift::ExtensionDecl& decl); codeql::ImportDecl translateImportDecl(const swift::ImportDecl& decl); std::optional translateModuleDecl(const swift::ModuleDecl& decl); + codeql::IfConfigDecl translateIfConfigDecl(const swift::IfConfigDecl& decl); + codeql::IfConfigClause translateIfConfigClause(const swift::IfConfigClause& clause); private: std::string mangledName(const swift::ValueDecl& decl); diff --git a/swift/extractor/visitors/SwiftVisitor.h b/swift/extractor/visitors/SwiftVisitor.h index 6380390af82..66c11b5227b 100644 --- a/swift/extractor/visitors/SwiftVisitor.h +++ b/swift/extractor/visitors/SwiftVisitor.h @@ -21,6 +21,7 @@ class SwiftVisitor : private SwiftDispatcher { private: void visit(swift::Decl* decl) override { declVisitor.visit(decl); } + void visit(const swift::IfConfigClause* clause) override { declVisitor.visit(clause); } void visit(swift::Stmt* stmt) override { stmtVisitor.visit(stmt); } void visit(swift::StmtCondition* cond) override { stmtVisitor.visitStmtCondition(cond); } void visit(swift::CaseLabelItem* item) override { stmtVisitor.visitCaseLabelItem(item); } diff --git a/swift/ql/lib/codeql/swift/elements.qll b/swift/ql/lib/codeql/swift/elements.qll index c10706d7c7a..bb3087936b9 100644 --- a/swift/ql/lib/codeql/swift/elements.qll +++ b/swift/ql/lib/codeql/swift/elements.qll @@ -24,6 +24,7 @@ import codeql.swift.elements.decl.FuncDecl import codeql.swift.elements.decl.GenericContext import codeql.swift.elements.decl.GenericTypeDecl import codeql.swift.elements.decl.GenericTypeParamDecl +import codeql.swift.elements.decl.IfConfigClause import codeql.swift.elements.decl.IfConfigDecl import codeql.swift.elements.decl.ImportDecl import codeql.swift.elements.decl.InfixOperatorDecl diff --git a/swift/ql/lib/codeql/swift/elements/decl/IfConfigClause.qll b/swift/ql/lib/codeql/swift/elements/decl/IfConfigClause.qll new file mode 100644 index 00000000000..6ab22e67989 --- /dev/null +++ b/swift/ql/lib/codeql/swift/elements/decl/IfConfigClause.qll @@ -0,0 +1,4 @@ +// generated by codegen/codegen.py, remove this comment if you wish to edit this file +private import codeql.swift.generated.decl.IfConfigClause + +class IfConfigClause extends IfConfigClauseBase { } diff --git a/swift/ql/lib/codeql/swift/elements/expr/UnresolvedDeclRefExpr.qll b/swift/ql/lib/codeql/swift/elements/expr/UnresolvedDeclRefExpr.qll index 281e1db01a7..3cbe55fe9a6 100644 --- a/swift/ql/lib/codeql/swift/elements/expr/UnresolvedDeclRefExpr.qll +++ b/swift/ql/lib/codeql/swift/elements/expr/UnresolvedDeclRefExpr.qll @@ -1,4 +1,9 @@ -// generated by codegen/codegen.py, remove this comment if you wish to edit this file private import codeql.swift.generated.expr.UnresolvedDeclRefExpr -class UnresolvedDeclRefExpr extends UnresolvedDeclRefExprBase { } +class UnresolvedDeclRefExpr extends UnresolvedDeclRefExprBase { + override string toString() { + result = getName() + " (unresolved)" + or + not hasName() and result = "(unresolved)" + } +} diff --git a/swift/ql/lib/codeql/swift/generated/GetImmediateParent.qll b/swift/ql/lib/codeql/swift/generated/GetImmediateParent.qll index 4cecd1a3ce6..9ca82a5e47d 100644 --- a/swift/ql/lib/codeql/swift/generated/GetImmediateParent.qll +++ b/swift/ql/lib/codeql/swift/generated/GetImmediateParent.qll @@ -28,6 +28,12 @@ Element getAnImmediateChild(Element e) { or enum_element_decl_params(e, _, x) or + if_config_clause_conditions(e, x) + or + if_config_clause_elements(e, _, x) + or + if_config_decl_clauses(e, _, x) + or pattern_binding_decl_inits(e, _, x) or pattern_binding_decl_patterns(e, _, x) diff --git a/swift/ql/lib/codeql/swift/generated/decl/IfConfigClause.qll b/swift/ql/lib/codeql/swift/generated/decl/IfConfigClause.qll new file mode 100644 index 00000000000..89a1cb277ed --- /dev/null +++ b/swift/ql/lib/codeql/swift/generated/decl/IfConfigClause.qll @@ -0,0 +1,30 @@ +// generated by codegen/codegen.py +import codeql.swift.elements.AstNode +import codeql.swift.elements.expr.Expr +import codeql.swift.elements.Locatable + +class IfConfigClauseBase extends @if_config_clause, Locatable { + override string getAPrimaryQlClass() { result = "IfConfigClause" } + + Expr getCondition() { + exists(Expr x | + if_config_clause_conditions(this, x) and + result = x.resolve() + ) + } + + predicate hasCondition() { exists(getCondition()) } + + AstNode getElement(int index) { + exists(AstNode x | + if_config_clause_elements(this, index, x) and + result = x.resolve() + ) + } + + AstNode getAnElement() { result = getElement(_) } + + int getNumberOfElements() { result = count(getAnElement()) } + + predicate isActive() { if_config_clause_is_active(this) } +} diff --git a/swift/ql/lib/codeql/swift/generated/decl/IfConfigDecl.qll b/swift/ql/lib/codeql/swift/generated/decl/IfConfigDecl.qll index 578051e9f8b..5d13959a227 100644 --- a/swift/ql/lib/codeql/swift/generated/decl/IfConfigDecl.qll +++ b/swift/ql/lib/codeql/swift/generated/decl/IfConfigDecl.qll @@ -1,6 +1,18 @@ // generated by codegen/codegen.py import codeql.swift.elements.decl.Decl +import codeql.swift.elements.decl.IfConfigClause class IfConfigDeclBase extends @if_config_decl, Decl { override string getAPrimaryQlClass() { result = "IfConfigDecl" } + + IfConfigClause getClause(int index) { + exists(IfConfigClause x | + if_config_decl_clauses(this, index, x) and + result = x.resolve() + ) + } + + IfConfigClause getAClause() { result = getClause(_) } + + int getNumberOfClauses() { result = count(getAClause()) } } diff --git a/swift/ql/lib/swift.dbscheme b/swift/ql/lib/swift.dbscheme index c12e00e028d..e181cef772a 100644 --- a/swift/ql/lib/swift.dbscheme +++ b/swift/ql/lib/swift.dbscheme @@ -36,6 +36,7 @@ files( @locatable = @ast_node | @condition_element +| @if_config_clause ; #keyset[id] @@ -643,6 +644,35 @@ if_config_decls( //dir=decl unique int id: @if_config_decl ); +#keyset[id, index] +if_config_decl_clauses( //dir=decl + int id: @if_config_decl ref, + int index: int ref, + int clause: @if_config_clause ref +); + +if_config_clauses( //dir=decl + unique int id: @if_config_clause +); + +#keyset[id] +if_config_clause_conditions( //dir=decl + int id: @if_config_clause ref, + int condition: @expr ref +); + +#keyset[id, index] +if_config_clause_elements( //dir=decl + int id: @if_config_clause ref, + int index: int ref, + int element: @ast_node ref +); + +#keyset[id] +if_config_clause_is_active( //dir=decl + int id: @if_config_clause ref +); + import_decls( //dir=decl unique int id: @import_decl, int module: @module_decl ref diff --git a/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause.expected b/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause.expected new file mode 100644 index 00000000000..d4230ce830c --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause.expected @@ -0,0 +1,6 @@ +| if_config.swift:1:1:1:1 | IfConfigClause | isActive: | no | +| if_config.swift:4:1:4:1 | IfConfigClause | isActive: | no | +| if_config.swift:7:1:7:1 | IfConfigClause | isActive: | yes | +| if_config_active.swift:3:1:3:1 | IfConfigClause | isActive: | yes | +| if_config_active.swift:6:1:6:1 | IfConfigClause | isActive: | no | +| if_config_active.swift:9:1:9:1 | IfConfigClause | isActive: | no | diff --git a/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause.ql b/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause.ql new file mode 100644 index 00000000000..832025712f0 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause.ql @@ -0,0 +1,10 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from IfConfigClause x, string isActive +where + toBeTested(x) and + not x.isUnknown() and + if x.isActive() then isActive = "yes" else isActive = "no" +select x, "isActive:", isActive diff --git a/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause_getCondition.expected b/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause_getCondition.expected new file mode 100644 index 00000000000..f4955665acd --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause_getCondition.expected @@ -0,0 +1,4 @@ +| if_config.swift:1:1:1:1 | IfConfigClause | if_config.swift:1:5:1:5 | FOO (unresolved) | +| if_config.swift:4:1:4:1 | IfConfigClause | if_config.swift:4:9:4:19 | call to ... | +| if_config_active.swift:3:1:3:1 | IfConfigClause | if_config_active.swift:3:5:3:5 | FOO (unresolved) | +| if_config_active.swift:6:1:6:1 | IfConfigClause | if_config_active.swift:6:9:6:17 | call to ... | diff --git a/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause_getCondition.ql b/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause_getCondition.ql new file mode 100644 index 00000000000..174c1a4994d --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause_getCondition.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from IfConfigClause x +where toBeTested(x) and not x.isUnknown() +select x, x.getCondition() diff --git a/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause_getElement.expected b/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause_getElement.expected new file mode 100644 index 00000000000..11dd2b54127 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause_getElement.expected @@ -0,0 +1,18 @@ +| if_config.swift:1:1:1:1 | IfConfigClause | 0 | if_config.swift:2:1:2:16 | { ... } | +| if_config.swift:1:1:1:1 | IfConfigClause | 1 | if_config.swift:2:5:2:5 | foo | +| if_config.swift:1:1:1:1 | IfConfigClause | 2 | if_config.swift:3:1:3:12 | { ... } | +| if_config.swift:4:1:4:1 | IfConfigClause | 0 | if_config.swift:5:1:5:16 | { ... } | +| if_config.swift:4:1:4:1 | IfConfigClause | 1 | if_config.swift:5:5:5:5 | bar | +| if_config.swift:4:1:4:1 | IfConfigClause | 2 | if_config.swift:6:1:6:12 | { ... } | +| if_config.swift:7:1:7:1 | IfConfigClause | 0 | if_config.swift:8:1:8:16 | { ... } | +| if_config.swift:7:1:7:1 | IfConfigClause | 1 | if_config.swift:8:5:8:5 | baz | +| if_config.swift:7:1:7:1 | IfConfigClause | 2 | if_config.swift:9:1:9:12 | { ... } | +| if_config_active.swift:3:1:3:1 | IfConfigClause | 0 | if_config_active.swift:4:1:4:16 | { ... } | +| if_config_active.swift:3:1:3:1 | IfConfigClause | 1 | if_config_active.swift:4:5:4:5 | foo | +| if_config_active.swift:3:1:3:1 | IfConfigClause | 2 | if_config_active.swift:5:1:5:12 | { ... } | +| if_config_active.swift:6:1:6:1 | IfConfigClause | 0 | if_config_active.swift:7:1:7:16 | { ... } | +| if_config_active.swift:6:1:6:1 | IfConfigClause | 1 | if_config_active.swift:7:5:7:5 | bar | +| if_config_active.swift:6:1:6:1 | IfConfigClause | 2 | if_config_active.swift:8:1:8:12 | { ... } | +| if_config_active.swift:9:1:9:1 | IfConfigClause | 0 | if_config_active.swift:10:1:10:16 | { ... } | +| if_config_active.swift:9:1:9:1 | IfConfigClause | 1 | if_config_active.swift:10:5:10:5 | baz | +| if_config_active.swift:9:1:9:1 | IfConfigClause | 2 | if_config_active.swift:11:1:11:12 | { ... } | diff --git a/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause_getElement.ql b/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause_getElement.ql new file mode 100644 index 00000000000..02c0eec17c9 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause_getElement.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from IfConfigClause x, int index +where toBeTested(x) and not x.isUnknown() +select x, index, x.getElement(index) diff --git a/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/if_config.swift b/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/if_config.swift new file mode 100644 index 00000000000..87cbc8ecd42 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/if_config.swift @@ -0,0 +1,10 @@ +#if FOO +var foo: Int = 1 +print("foo") +#elseif os(watchOS) +var bar: Int = 2 +print("bar") +#else +var baz: Int = 3 +print("baz") +#endif diff --git a/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/if_config_active.swift b/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/if_config_active.swift new file mode 100644 index 00000000000..89849731bfe --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/if_config_active.swift @@ -0,0 +1,12 @@ +//codeql-extractor-options: -D FOO + +#if FOO +var foo: Int = 1 +print("foo") +#elseif os(macOS) +var bar: Int = 2 +print("bar") +#else +var baz: Int = 3 +print("baz") +#endif diff --git a/swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/IfConfigDecl.expected b/swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/IfConfigDecl.expected new file mode 100644 index 00000000000..3fed7a7fcc1 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/IfConfigDecl.expected @@ -0,0 +1 @@ +| if_config.swift:1:1:10:1 | #if ... | diff --git a/swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/IfConfigDecl.ql b/swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/IfConfigDecl.ql new file mode 100644 index 00000000000..a43d57b1d93 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/IfConfigDecl.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from IfConfigDecl x +where toBeTested(x) and not x.isUnknown() +select x diff --git a/swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/IfConfigDecl_getClause.expected b/swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/IfConfigDecl_getClause.expected new file mode 100644 index 00000000000..d90ba458397 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/IfConfigDecl_getClause.expected @@ -0,0 +1,3 @@ +| if_config.swift:1:1:10:1 | #if ... | 0 | if_config.swift:1:1:1:1 | IfConfigClause | +| if_config.swift:1:1:10:1 | #if ... | 1 | if_config.swift:4:1:4:1 | IfConfigClause | +| if_config.swift:1:1:10:1 | #if ... | 2 | if_config.swift:7:1:7:1 | IfConfigClause | diff --git a/swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/IfConfigDecl_getClause.ql b/swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/IfConfigDecl_getClause.ql new file mode 100644 index 00000000000..5ffaf75e476 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/IfConfigDecl_getClause.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from IfConfigDecl x, int index +where toBeTested(x) and not x.isUnknown() +select x, index, x.getClause(index) diff --git a/swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/if_config.swift b/swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/if_config.swift new file mode 100644 index 00000000000..6f492013933 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/if_config.swift @@ -0,0 +1,10 @@ +#if FOO +var foo: Int = 1 +print("foo") +#elseif BAR +var bar: Int = 2 +print("bar") +#else +var baz: Int = 3 +print("baz") +#endif diff --git a/swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/UnresolvedDeclRefExpr.expected b/swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/UnresolvedDeclRefExpr.expected new file mode 100644 index 00000000000..ecf2a57ba5d --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/UnresolvedDeclRefExpr.expected @@ -0,0 +1,10 @@ +| unresolved_decl_ref.swift:4:5:4:5 | FOO (unresolved) | +| unresolved_decl_ref.swift:4:9:4:9 | && (unresolved) | +| unresolved_decl_ref.swift:4:12:4:12 | os (unresolved) | +| unresolved_decl_ref.swift:4:15:4:15 | Windows (unresolved) | +| unresolved_decl_ref.swift:5:1:5:1 | print (unresolved) | +| unresolved_decl_ref.swift:6:9:6:9 | BAR (unresolved) | +| unresolved_decl_ref.swift:6:13:6:13 | \|\| (unresolved) | +| unresolved_decl_ref.swift:6:16:6:16 | arch (unresolved) | +| unresolved_decl_ref.swift:6:21:6:21 | i386 (unresolved) | +| unresolved_decl_ref.swift:9:1:9:1 | print (unresolved) | diff --git a/swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/UnresolvedDeclRefExpr.ql b/swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/UnresolvedDeclRefExpr.ql new file mode 100644 index 00000000000..507d2fcf87d --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/UnresolvedDeclRefExpr.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from UnresolvedDeclRefExpr x +where toBeTested(x) and not x.isUnknown() +select x diff --git a/swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/UnresolvedDeclRefExpr_getName.expected b/swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/UnresolvedDeclRefExpr_getName.expected new file mode 100644 index 00000000000..1ea60aabedc --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/UnresolvedDeclRefExpr_getName.expected @@ -0,0 +1,10 @@ +| unresolved_decl_ref.swift:4:5:4:5 | FOO (unresolved) | FOO | +| unresolved_decl_ref.swift:4:9:4:9 | && (unresolved) | && | +| unresolved_decl_ref.swift:4:12:4:12 | os (unresolved) | os | +| unresolved_decl_ref.swift:4:15:4:15 | Windows (unresolved) | Windows | +| unresolved_decl_ref.swift:5:1:5:1 | print (unresolved) | print | +| unresolved_decl_ref.swift:6:9:6:9 | BAR (unresolved) | BAR | +| unresolved_decl_ref.swift:6:13:6:13 | \|\| (unresolved) | \|\| | +| unresolved_decl_ref.swift:6:16:6:16 | arch (unresolved) | arch | +| unresolved_decl_ref.swift:6:21:6:21 | i386 (unresolved) | i386 | +| unresolved_decl_ref.swift:9:1:9:1 | print (unresolved) | print | diff --git a/swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/UnresolvedDeclRefExpr_getName.ql b/swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/UnresolvedDeclRefExpr_getName.ql new file mode 100644 index 00000000000..1424e3443d9 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/UnresolvedDeclRefExpr_getName.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from UnresolvedDeclRefExpr x +where toBeTested(x) and not x.isUnknown() +select x, x.getName() diff --git a/swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/UnresolvedDeclRefExpr_getType.expected b/swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/UnresolvedDeclRefExpr_getType.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/UnresolvedDeclRefExpr_getType.ql b/swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/UnresolvedDeclRefExpr_getType.ql new file mode 100644 index 00000000000..27953cceeb1 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/UnresolvedDeclRefExpr_getType.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from UnresolvedDeclRefExpr x +where toBeTested(x) and not x.isUnknown() +select x, x.getType() diff --git a/swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/unresolved_decl_ref.swift b/swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/unresolved_decl_ref.swift new file mode 100644 index 00000000000..94dee19d618 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/unresolved_decl_ref.swift @@ -0,0 +1,10 @@ +//codeql-extractor-options: -D BAR + +// conditions and inactive branches in conditional compilation blocks are not resolved +#if FOO && os(Windows) +print(1) +#elseif BAR || arch(i386) +print(2) +#else +print(3) +#endif From 93d06daf6738843472c72c4ae80e3396a08d1891 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Mon, 11 Jul 2022 15:59:21 +0200 Subject: [PATCH 168/505] Swift: allow skipping fields in cppgen Some fields of base classes pose some problems with diamond hierarchies, and we don't use them any way as we are emitting them using directly trap entries instead of structured C++ classes. This introduces a `cpp_skip` pragma to skip generation of those fields in structured generated C++ classes, and applies it to `is_unknown` and `location`. --- swift/codegen/generators/cppgen.py | 38 +++++++++++++++++------------- swift/codegen/schema.yml | 8 +++++-- swift/codegen/test/test_cppgen.py | 13 ++++++++++ 3 files changed, 40 insertions(+), 19 deletions(-) diff --git a/swift/codegen/generators/cppgen.py b/swift/codegen/generators/cppgen.py index ef042f42dbd..08af5fb82f9 100644 --- a/swift/codegen/generators/cppgen.py +++ b/swift/codegen/generators/cppgen.py @@ -13,6 +13,7 @@ Each class in the schema gets a corresponding `struct` in `TrapClasses.h`, where import functools import pathlib +import typing from typing import Dict import inflection @@ -34,22 +35,25 @@ def _get_type(t: str) -> str: return t -def _get_field(cls: schema.Class, p: schema.Property) -> cpp.Field: - trap_name = None - if not p.is_single: - trap_name = inflection.camelize(f"{cls.name}_{p.name}") - if not p.is_predicate: - trap_name = inflection.pluralize(trap_name) - args = dict( - field_name=p.name + ("_" if p.name in cpp.cpp_keywords else ""), - type=_get_type(p.type), - is_optional=p.is_optional, - is_repeated=p.is_repeated, - is_predicate=p.is_predicate, - trap_name=trap_name, - ) - args.update(cpp.get_field_override(p.name)) - return cpp.Field(**args) +def _get_fields(cls: schema.Class) -> typing.Iterable[cpp.Field]: + for p in cls.properties: + if "cpp_skip" in p.pragmas: + continue + trap_name = None + if not p.is_single: + trap_name = inflection.camelize(f"{cls.name}_{p.name}") + if not p.is_predicate: + trap_name = inflection.pluralize(trap_name) + args = dict( + field_name=p.name + ("_" if p.name in cpp.cpp_keywords else ""), + type=_get_type(p.type), + is_optional=p.is_optional, + is_repeated=p.is_repeated, + is_predicate=p.is_predicate, + trap_name=trap_name, + ) + args.update(cpp.get_field_override(p.name)) + yield cpp.Field(**args) class Processor: @@ -65,7 +69,7 @@ class Processor: return cpp.Class( name=name, bases=[self._get_class(b) for b in cls.bases], - fields=[_get_field(cls, p) for p in cls.properties], + fields=list(_get_fields(cls)), final=not cls.derived, trap_name=trap_name, ) diff --git a/swift/codegen/schema.yml b/swift/codegen/schema.yml index 01bb0c2feba..ed3d4433a9f 100644 --- a/swift/codegen/schema.yml +++ b/swift/codegen/schema.yml @@ -13,14 +13,18 @@ _directories: stmt: Stmt$ Element: - is_unknown: predicate + is_unknown: + type: predicate + _pragma: cpp_skip # this is emitted using trap entries directly _pragma: qltest_skip File: name: string Locatable: - location: Location? + location: + type: Location? + _pragma: cpp_skip # this is emitted using trap entries directly _pragma: qltest_skip Location: diff --git a/swift/codegen/test/test_cppgen.py b/swift/codegen/test/test_cppgen.py index 160dd6cd2c1..7938cb6decd 100644 --- a/swift/codegen/test/test_cppgen.py +++ b/swift/codegen/test/test_cppgen.py @@ -165,5 +165,18 @@ def test_classes_with_dirs(generate_grouped): } +def test_cpp_skip_pragma(generate): + assert generate([ + schema.Class(name="A", properties=[ + schema.SingleProperty("x", "foo"), + schema.SingleProperty("y", "bar", pragmas=["x", "cpp_skip", "y"]), + ]) + ]) == [ + cpp.Class(name="A", final=True, trap_name="As", fields=[ + cpp.Field("x", "foo"), + ]), + ] + + if __name__ == '__main__': sys.exit(pytest.main([__file__] + sys.argv[1:])) From 348ad95fc001b154f9640108b8d04e7ececdc56c Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Mon, 11 Jul 2022 15:06:27 +0100 Subject: [PATCH 169/505] Ruby: fix defining every dataflow node as a command execution sink --- ruby/ql/lib/codeql/ruby/frameworks/Railties.qll | 4 ++-- .../library-tests/frameworks/railties/Railties.expected | 9 +++++++++ .../test/library-tests/frameworks/railties/Railties.ql | 5 +++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/frameworks/Railties.qll b/ruby/ql/lib/codeql/ruby/frameworks/Railties.qll index 4a61eb1dd20..807cc9f9c51 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/Railties.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/Railties.qll @@ -43,7 +43,7 @@ module Railties { override DataFlow::Node getAnArgument() { result = this.getArgument([0, 1]) } - override predicate isShellInterpreted(DataFlow::Node arg) { any() } + override predicate isShellInterpreted(DataFlow::Node arg) { arg = this.getAnArgument() } } /** @@ -57,6 +57,6 @@ module Railties { override DataFlow::Node getAnArgument() { result = this.getArgument(0) } - override predicate isShellInterpreted(DataFlow::Node arg) { any() } + override predicate isShellInterpreted(DataFlow::Node arg) { arg = this.getAnArgument() } } } diff --git a/ruby/ql/test/library-tests/frameworks/railties/Railties.expected b/ruby/ql/test/library-tests/frameworks/railties/Railties.expected index c012090bbe1..42cac29b7c5 100644 --- a/ruby/ql/test/library-tests/frameworks/railties/Railties.expected +++ b/ruby/ql/test/library-tests/frameworks/railties/Railties.expected @@ -1,5 +1,14 @@ +systemCommandExecutions | Railties.rb:5:5:5:34 | call to execute_command | | Railties.rb:6:5:6:37 | call to execute_command | | Railties.rb:8:5:8:16 | call to rake | | Railties.rb:10:5:10:27 | call to rails_command | | Railties.rb:12:5:12:17 | call to git | +shellInterpretedArguments +| Railties.rb:5:5:5:34 | call to execute_command | Railties.rb:5:21:5:25 | :rake | +| Railties.rb:5:5:5:34 | call to execute_command | Railties.rb:5:28:5:33 | "test" | +| Railties.rb:6:5:6:37 | call to execute_command | Railties.rb:6:21:6:26 | :rails | +| Railties.rb:6:5:6:37 | call to execute_command | Railties.rb:6:29:6:36 | "server" | +| Railties.rb:8:5:8:16 | call to rake | Railties.rb:8:10:8:15 | "test" | +| Railties.rb:10:5:10:27 | call to rails_command | Railties.rb:10:19:10:26 | "server" | +| Railties.rb:12:5:12:17 | call to git | Railties.rb:12:9:12:16 | "status" | diff --git a/ruby/ql/test/library-tests/frameworks/railties/Railties.ql b/ruby/ql/test/library-tests/frameworks/railties/Railties.ql index 9a9731befb4..4dee50abcd5 100644 --- a/ruby/ql/test/library-tests/frameworks/railties/Railties.ql +++ b/ruby/ql/test/library-tests/frameworks/railties/Railties.ql @@ -1,5 +1,10 @@ private import ruby private import codeql.ruby.Concepts private import codeql.ruby.frameworks.Railties +private import codeql.ruby.DataFlow query predicate systemCommandExecutions(SystemCommandExecution e) { any() } + +query predicate shellInterpretedArguments(SystemCommandExecution e, DataFlow::Node arg) { + e.isShellInterpreted(arg) +} From 156bc34cdadbd1913cc2c3ae93fc0953c04c5bdc Mon Sep 17 00:00:00 2001 From: Raul Garcia <42392023+raulgarciamsft@users.noreply.github.com> Date: Mon, 11 Jul 2022 08:41:05 -0700 Subject: [PATCH 170/505] Update UnsafeUsageOfClientSideEncryptionVersion.qhelp --- .../Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp b/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp index 49aa5623570..a3a33691854 100644 --- a/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp +++ b/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp @@ -14,11 +14,6 @@ -

    The following example shows an HTTP request parameter being used directly in a forming a -new request without validating the input, which facilitates SSRF attacks. -It also shows how to remedy the problem by validating the user input against a known fixed string. -

    -
    From 5d89a5d164f111e9080d711e16a903b8d7af291f Mon Sep 17 00:00:00 2001 From: Raul Garcia <42392023+raulgarciamsft@users.noreply.github.com> Date: Mon, 11 Jul 2022 08:42:50 -0700 Subject: [PATCH 171/505] Update csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql Co-authored-by: Taus --- .../CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql b/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql index 0de3d020e4f..095c154e9a3 100644 --- a/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql +++ b/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql @@ -14,7 +14,7 @@ import csharp /** * Holds if `oc` is creating an object of type `c` = `Azure.Storage.ClientSideEncryptionOptions` - * and `e` is the `version` argument to the contructor + * and `e` is the `version` argument to the constructor */ predicate isCreatingAzureClientSideEncryptionObject(ObjectCreation oc, Class c, Expr e) { exists(Parameter p | p.hasName("version") | From 6632dfaf88eaa2dd266ee5dfa745142678844de4 Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Mon, 11 Jul 2022 16:34:38 +0100 Subject: [PATCH 172/505] Ruby: fix another SystemCommandExecution::isShellInterpreted implementation --- ruby/ql/lib/codeql/ruby/frameworks/PosixSpawn.qll | 2 +- ruby/ql/test/library-tests/frameworks/PosixSpawn.ql | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/frameworks/PosixSpawn.qll b/ruby/ql/lib/codeql/ruby/frameworks/PosixSpawn.qll index 6a27018fcf5..6c4d2ab1a47 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/PosixSpawn.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/PosixSpawn.qll @@ -62,9 +62,9 @@ module PosixSpawn { // is shell interpreted unless there is another argument with a string // constant value. override predicate isShellInterpreted(DataFlow::Node arg) { + this.argument(arg) and not exists(DataFlow::Node otherArg | otherArg != arg and - this.argument(arg) and this.argument(otherArg) and otherArg.asExpr().getConstantValue().isString(_) ) diff --git a/ruby/ql/test/library-tests/frameworks/PosixSpawn.ql b/ruby/ql/test/library-tests/frameworks/PosixSpawn.ql index 12fb445cf15..994f0d162f0 100644 --- a/ruby/ql/test/library-tests/frameworks/PosixSpawn.ql +++ b/ruby/ql/test/library-tests/frameworks/PosixSpawn.ql @@ -5,11 +5,13 @@ import codeql.ruby.DataFlow query predicate systemCalls( PosixSpawn::SystemCall call, DataFlow::Node arg, boolean shellInterpreted ) { - arg = call.getAnArgument() and - if call.isShellInterpreted(arg) then shellInterpreted = true else shellInterpreted = false + call.isShellInterpreted(arg) and shellInterpreted = true + or + not call.isShellInterpreted(arg) and arg = call.getAnArgument() and shellInterpreted = false } query predicate childCalls(PosixSpawn::ChildCall call, DataFlow::Node arg, boolean shellInterpreted) { - arg = call.getAnArgument() and - if call.isShellInterpreted(arg) then shellInterpreted = true else shellInterpreted = false + call.isShellInterpreted(arg) and shellInterpreted = true + or + not call.isShellInterpreted(arg) and arg = call.getAnArgument() and shellInterpreted = false } From 032aa56dc3abbd8bfbab44212ecc730ebd7ae7d0 Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Mon, 11 Jul 2022 17:00:07 +0100 Subject: [PATCH 173/505] Ruby: add change note for system command execution sink bug --- ruby/ql/lib/change-notes/2022-07-11-command-execution.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ruby/ql/lib/change-notes/2022-07-11-command-execution.md diff --git a/ruby/ql/lib/change-notes/2022-07-11-command-execution.md b/ruby/ql/lib/change-notes/2022-07-11-command-execution.md new file mode 100644 index 00000000000..c95b9afb133 --- /dev/null +++ b/ruby/ql/lib/change-notes/2022-07-11-command-execution.md @@ -0,0 +1,6 @@ +--- +category: minorAnalysis +--- +* Fixed a bug causing every expression in the database to be a considered a system-command execution sink when calls to any of the following methods exist: + * The `spawn", "fspawn", "popen4", "pspawn", "system", "_pspawn" methods and the backtick operator from the `POSIX::spawn` gem. + * The `execute_command`, `rake`, `rails_command`, and `git` methods in `Rails::Generation::Actions`. From a3628b06f1f0efa57d6f705259c53aee736bc96c Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Mon, 11 Jul 2022 17:23:45 +0100 Subject: [PATCH 174/505] Ruby: fix markup in changenote --- ruby/ql/lib/change-notes/2022-07-11-command-execution.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruby/ql/lib/change-notes/2022-07-11-command-execution.md b/ruby/ql/lib/change-notes/2022-07-11-command-execution.md index c95b9afb133..2c5addba1f5 100644 --- a/ruby/ql/lib/change-notes/2022-07-11-command-execution.md +++ b/ruby/ql/lib/change-notes/2022-07-11-command-execution.md @@ -2,5 +2,5 @@ category: minorAnalysis --- * Fixed a bug causing every expression in the database to be a considered a system-command execution sink when calls to any of the following methods exist: - * The `spawn", "fspawn", "popen4", "pspawn", "system", "_pspawn" methods and the backtick operator from the `POSIX::spawn` gem. + * The `spawn`, `fspawn`, `popen4`, `pspawn`, `system`, `_pspawn` methods and the backtick operator from the `POSIX::spawn` gem. * The `execute_command`, `rake`, `rails_command`, and `git` methods in `Rails::Generation::Actions`. From 4704269086c6fadec3d97c7ad16bf056946d1ae8 Mon Sep 17 00:00:00 2001 From: Henry Mercer Date: Mon, 11 Jul 2022 18:36:03 +0100 Subject: [PATCH 175/505] Add example registry authentication string --- .../codeql-cli/publishing-and-using-codeql-packs.rst | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/codeql/codeql-cli/publishing-and-using-codeql-packs.rst b/docs/codeql/codeql-cli/publishing-and-using-codeql-packs.rst index 67e90b5ba3f..985ca2659f3 100644 --- a/docs/codeql/codeql-cli/publishing-and-using-codeql-packs.rst +++ b/docs/codeql/codeql-cli/publishing-and-using-codeql-packs.rst @@ -103,7 +103,7 @@ You can now use ``codeql pack publish``, ``codeql pack download``, and ``codeql Authenticating to GitHub Container registries --------------------------------------------- -You can download a private pack or publish a pack by authenticating to the appropriate GitHub Container registry. +You can publish packs and download private packs by authenticating to the appropriate GitHub Container registry. You can authenticate to the Container registry on GitHub.com in two ways: @@ -115,5 +115,10 @@ Similarly, you can authenticate to a GHES Container registry, or authenticate to 1. Pass the ``--registries-auth-stdin`` option to the CodeQL CLI, then supply a registry authentication string via standard input. 2. Set the ``CODEQL_REGISTRIES_AUTH`` environment variable to a registry authentication string. -A registry authentication string is a comma-separated list of ``=`` pairs, where ``registry-url`` is a GitHub Container registry URL, for example ``https://containers.GHE_HOSTNAME/v2/`` and ``token`` is a GitHub Apps token or personal access token for that GitHub Container registry. +A registry authentication string is a comma-separated list of ``=`` pairs, where ``registry-url`` is a GitHub Container registry URL, such as ``https://containers.GHE_HOSTNAME/v2/``, and ``token`` is a GitHub Apps token or personal access token for that GitHub Container registry. This ensures that each token is only passed to the Container registry you specify. +For instance, the following registry authentication string specifies that the CodeQL CLI should authenticate to the Container registry on GitHub.com using the token ```` and to the Container registry for the GHES instance at ``GHE_HOSTNAME`` using the token ````: + +.. code-block:: none + + https://ghcr.io/v2/=,https://containers.GHE_HOSTNAME/v2/= From b9072a35943c1f6845124ed71c995e1dfca2cb32 Mon Sep 17 00:00:00 2001 From: Ian Lynagh Date: Mon, 11 Jul 2022 18:57:43 +0100 Subject: [PATCH 176/505] Kotlin: Share a Psi2Ir instance --- .../src/main/kotlin/comments/CommentExtractor.kt | 5 +++-- .../src/main/kotlin/utils/IrVisitorLookup.kt | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/java/kotlin-extractor/src/main/kotlin/comments/CommentExtractor.kt b/java/kotlin-extractor/src/main/kotlin/comments/CommentExtractor.kt index 93d3c0534de..f3fef96a338 100644 --- a/java/kotlin-extractor/src/main/kotlin/comments/CommentExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/comments/CommentExtractor.kt @@ -16,7 +16,8 @@ import org.jetbrains.kotlin.psi.psiUtil.startOffset class CommentExtractor(private val fileExtractor: KotlinFileExtractor, private val file: IrFile, private val fileLabel: Label) { private val tw = fileExtractor.tw private val logger = fileExtractor.logger - private val ktFile = Psi2Ir().getKtFile(file) + private val psi2Ir = Psi2Ir() + private val ktFile = psi2Ir.getKtFile(file) fun extract() { if (ktFile == null) { @@ -85,7 +86,7 @@ class CommentExtractor(private val fileExtractor: KotlinFileExtractor, private v val ownerPsi = getKDocOwner(comment) ?: return val owners = mutableListOf() - file.accept(IrVisitorLookup(ownerPsi, file), owners) + file.accept(IrVisitorLookup(psi2Ir, ownerPsi, file), owners) for (ownerIr in owners) { val ownerLabel = diff --git a/java/kotlin-extractor/src/main/kotlin/utils/IrVisitorLookup.kt b/java/kotlin-extractor/src/main/kotlin/utils/IrVisitorLookup.kt index 0020d00fac4..a25c1a4534f 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/IrVisitorLookup.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/IrVisitorLookup.kt @@ -8,7 +8,7 @@ import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.util.isFakeOverride import org.jetbrains.kotlin.ir.visitors.IrElementVisitor -class IrVisitorLookup(private val psi: PsiElement, private val file: IrFile) : +class IrVisitorLookup(private val psi2Ir: Psi2Ir, private val psi: PsiElement, private val file: IrFile) : IrElementVisitor> { private val location = psi.getLocation() @@ -27,7 +27,7 @@ class IrVisitorLookup(private val psi: PsiElement, private val file: IrFile) : } if (location.contains(elementLocation)) { - val psiElement = Psi2Ir().findPsiElement(element, file) + val psiElement = psi2Ir.findPsiElement(element, file) if (psiElement == psi) { // There can be multiple IrElements that match the same PSI element. data.add(element) @@ -35,4 +35,4 @@ class IrVisitorLookup(private val psi: PsiElement, private val file: IrFile) : } element.acceptChildren(this, data) } -} \ No newline at end of file +} From 4c68624b00bff04949c11f4d7b408c97fc8f71c8 Mon Sep 17 00:00:00 2001 From: Ian Lynagh Date: Mon, 11 Jul 2022 19:17:21 +0100 Subject: [PATCH 177/505] Kotlin: Pass a FileLogger to Psi2Ir --- .../src/main/kotlin/comments/CommentExtractor.kt | 2 +- .../src/main/kotlin/utils/versions/v_1_4_32/Psi2Ir.kt | 5 +++-- .../src/main/kotlin/utils/versions/v_1_5_0/Psi2Ir.kt | 5 +++-- .../src/main/kotlin/utils/versions/v_1_5_10/Psi2Ir.kt | 5 +++-- .../src/main/kotlin/utils/versions/v_1_5_21/Psi2Ir.kt | 5 +++-- .../src/main/kotlin/utils/versions/v_1_5_31/Psi2Ir.kt | 5 +++-- .../src/main/kotlin/utils/versions/v_1_6_10/Psi2Ir.kt | 5 +++-- .../src/main/kotlin/utils/versions/v_1_6_20/Psi2Ir.kt | 5 +++-- .../src/main/kotlin/utils/versions/v_1_7_0/Psi2Ir.kt | 5 +++-- 9 files changed, 25 insertions(+), 17 deletions(-) diff --git a/java/kotlin-extractor/src/main/kotlin/comments/CommentExtractor.kt b/java/kotlin-extractor/src/main/kotlin/comments/CommentExtractor.kt index f3fef96a338..03f032f8810 100644 --- a/java/kotlin-extractor/src/main/kotlin/comments/CommentExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/comments/CommentExtractor.kt @@ -16,7 +16,7 @@ import org.jetbrains.kotlin.psi.psiUtil.startOffset class CommentExtractor(private val fileExtractor: KotlinFileExtractor, private val file: IrFile, private val fileLabel: Label) { private val tw = fileExtractor.tw private val logger = fileExtractor.logger - private val psi2Ir = Psi2Ir() + private val psi2Ir = Psi2Ir(logger) private val ktFile = psi2Ir.getKtFile(file) fun extract() { diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_4_32/Psi2Ir.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_4_32/Psi2Ir.kt index 16ee288d05a..392e7cf6c28 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_4_32/Psi2Ir.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_4_32/Psi2Ir.kt @@ -1,12 +1,13 @@ package com.github.codeql.utils.versions +import com.github.codeql.FileLogger import com.intellij.psi.PsiElement import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi2ir.PsiSourceManager -class Psi2Ir : Psi2IrFacade { +class Psi2Ir(private val logger: FileLogger) : Psi2IrFacade { companion object { val psiManager = PsiSourceManager() } @@ -18,4 +19,4 @@ class Psi2Ir : Psi2IrFacade { override fun findPsiElement(irElement: IrElement, irFile: IrFile): PsiElement? { return psiManager.findPsiElement(irElement, irFile) } -} \ No newline at end of file +} diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_5_0/Psi2Ir.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_5_0/Psi2Ir.kt index 16ee288d05a..392e7cf6c28 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_5_0/Psi2Ir.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_5_0/Psi2Ir.kt @@ -1,12 +1,13 @@ package com.github.codeql.utils.versions +import com.github.codeql.FileLogger import com.intellij.psi.PsiElement import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi2ir.PsiSourceManager -class Psi2Ir : Psi2IrFacade { +class Psi2Ir(private val logger: FileLogger) : Psi2IrFacade { companion object { val psiManager = PsiSourceManager() } @@ -18,4 +19,4 @@ class Psi2Ir : Psi2IrFacade { override fun findPsiElement(irElement: IrElement, irFile: IrFile): PsiElement? { return psiManager.findPsiElement(irElement, irFile) } -} \ No newline at end of file +} diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_5_10/Psi2Ir.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_5_10/Psi2Ir.kt index 16ee288d05a..392e7cf6c28 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_5_10/Psi2Ir.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_5_10/Psi2Ir.kt @@ -1,12 +1,13 @@ package com.github.codeql.utils.versions +import com.github.codeql.FileLogger import com.intellij.psi.PsiElement import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi2ir.PsiSourceManager -class Psi2Ir : Psi2IrFacade { +class Psi2Ir(private val logger: FileLogger) : Psi2IrFacade { companion object { val psiManager = PsiSourceManager() } @@ -18,4 +19,4 @@ class Psi2Ir : Psi2IrFacade { override fun findPsiElement(irElement: IrElement, irFile: IrFile): PsiElement? { return psiManager.findPsiElement(irElement, irFile) } -} \ No newline at end of file +} diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_5_21/Psi2Ir.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_5_21/Psi2Ir.kt index 256a8b3bb63..8e21191f2a0 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_5_21/Psi2Ir.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_5_21/Psi2Ir.kt @@ -1,5 +1,6 @@ package com.github.codeql.utils.versions +import com.github.codeql.FileLogger import com.intellij.psi.PsiElement import org.jetbrains.kotlin.backend.common.psi.PsiSourceManager import org.jetbrains.kotlin.backend.jvm.ir.getKtFile @@ -7,7 +8,7 @@ import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.psi.KtFile -class Psi2Ir: Psi2IrFacade { +class Psi2Ir(private val logger: FileLogger): Psi2IrFacade { override fun getKtFile(irFile: IrFile): KtFile? { return irFile.getKtFile() } @@ -15,4 +16,4 @@ class Psi2Ir: Psi2IrFacade { override fun findPsiElement(irElement: IrElement, irFile: IrFile): PsiElement? { return PsiSourceManager.findPsiElement(irElement, irFile) } -} \ No newline at end of file +} diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_5_31/Psi2Ir.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_5_31/Psi2Ir.kt index 256a8b3bb63..8e21191f2a0 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_5_31/Psi2Ir.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_5_31/Psi2Ir.kt @@ -1,5 +1,6 @@ package com.github.codeql.utils.versions +import com.github.codeql.FileLogger import com.intellij.psi.PsiElement import org.jetbrains.kotlin.backend.common.psi.PsiSourceManager import org.jetbrains.kotlin.backend.jvm.ir.getKtFile @@ -7,7 +8,7 @@ import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.psi.KtFile -class Psi2Ir: Psi2IrFacade { +class Psi2Ir(private val logger: FileLogger): Psi2IrFacade { override fun getKtFile(irFile: IrFile): KtFile? { return irFile.getKtFile() } @@ -15,4 +16,4 @@ class Psi2Ir: Psi2IrFacade { override fun findPsiElement(irElement: IrElement, irFile: IrFile): PsiElement? { return PsiSourceManager.findPsiElement(irElement, irFile) } -} \ No newline at end of file +} diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_6_10/Psi2Ir.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_6_10/Psi2Ir.kt index 256a8b3bb63..8e21191f2a0 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_6_10/Psi2Ir.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_6_10/Psi2Ir.kt @@ -1,5 +1,6 @@ package com.github.codeql.utils.versions +import com.github.codeql.FileLogger import com.intellij.psi.PsiElement import org.jetbrains.kotlin.backend.common.psi.PsiSourceManager import org.jetbrains.kotlin.backend.jvm.ir.getKtFile @@ -7,7 +8,7 @@ import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.psi.KtFile -class Psi2Ir: Psi2IrFacade { +class Psi2Ir(private val logger: FileLogger): Psi2IrFacade { override fun getKtFile(irFile: IrFile): KtFile? { return irFile.getKtFile() } @@ -15,4 +16,4 @@ class Psi2Ir: Psi2IrFacade { override fun findPsiElement(irElement: IrElement, irFile: IrFile): PsiElement? { return PsiSourceManager.findPsiElement(irElement, irFile) } -} \ No newline at end of file +} diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_6_20/Psi2Ir.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_6_20/Psi2Ir.kt index 256a8b3bb63..8e21191f2a0 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_6_20/Psi2Ir.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_6_20/Psi2Ir.kt @@ -1,5 +1,6 @@ package com.github.codeql.utils.versions +import com.github.codeql.FileLogger import com.intellij.psi.PsiElement import org.jetbrains.kotlin.backend.common.psi.PsiSourceManager import org.jetbrains.kotlin.backend.jvm.ir.getKtFile @@ -7,7 +8,7 @@ import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.psi.KtFile -class Psi2Ir: Psi2IrFacade { +class Psi2Ir(private val logger: FileLogger): Psi2IrFacade { override fun getKtFile(irFile: IrFile): KtFile? { return irFile.getKtFile() } @@ -15,4 +16,4 @@ class Psi2Ir: Psi2IrFacade { override fun findPsiElement(irElement: IrElement, irFile: IrFile): PsiElement? { return PsiSourceManager.findPsiElement(irElement, irFile) } -} \ No newline at end of file +} diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_7_0/Psi2Ir.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_7_0/Psi2Ir.kt index 256a8b3bb63..8e21191f2a0 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_7_0/Psi2Ir.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_7_0/Psi2Ir.kt @@ -1,5 +1,6 @@ package com.github.codeql.utils.versions +import com.github.codeql.FileLogger import com.intellij.psi.PsiElement import org.jetbrains.kotlin.backend.common.psi.PsiSourceManager import org.jetbrains.kotlin.backend.jvm.ir.getKtFile @@ -7,7 +8,7 @@ import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.psi.KtFile -class Psi2Ir: Psi2IrFacade { +class Psi2Ir(private val logger: FileLogger): Psi2IrFacade { override fun getKtFile(irFile: IrFile): KtFile? { return irFile.getKtFile() } @@ -15,4 +16,4 @@ class Psi2Ir: Psi2IrFacade { override fun findPsiElement(irElement: IrElement, irFile: IrFile): PsiElement? { return PsiSourceManager.findPsiElement(irElement, irFile) } -} \ No newline at end of file +} From 960d1dba8a5dd72af2f3d070646dcea56b71cb20 Mon Sep 17 00:00:00 2001 From: Ian Lynagh Date: Mon, 11 Jul 2022 19:36:43 +0100 Subject: [PATCH 178/505] Kotlin: We can't etract comments for < 1.5.20 We were making our own PsiSourceManager, but that didn't know about any IrFile -> PsiFile mappings. --- .../src/main/kotlin/utils/versions/v_1_4_32/Psi2Ir.kt | 11 ++++------- .../src/main/kotlin/utils/versions/v_1_5_0/Psi2Ir.kt | 11 ++++------- .../src/main/kotlin/utils/versions/v_1_5_10/Psi2Ir.kt | 11 ++++------- 3 files changed, 12 insertions(+), 21 deletions(-) diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_4_32/Psi2Ir.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_4_32/Psi2Ir.kt index 392e7cf6c28..fce55c87cd6 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_4_32/Psi2Ir.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_4_32/Psi2Ir.kt @@ -5,18 +5,15 @@ import com.intellij.psi.PsiElement import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.psi.KtFile -import org.jetbrains.kotlin.psi2ir.PsiSourceManager class Psi2Ir(private val logger: FileLogger) : Psi2IrFacade { - companion object { - val psiManager = PsiSourceManager() - } - override fun getKtFile(irFile: IrFile): KtFile? { - return psiManager.getKtFile(irFile) + logger.warn("Comment extraction is not supported for Kotlin < 1.5.20") + return null } override fun findPsiElement(irElement: IrElement, irFile: IrFile): PsiElement? { - return psiManager.findPsiElement(irElement, irFile) + logger.error("Attempted comment extraction for Kotlin < 1.5.20") + return null } } diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_5_0/Psi2Ir.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_5_0/Psi2Ir.kt index 392e7cf6c28..fce55c87cd6 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_5_0/Psi2Ir.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_5_0/Psi2Ir.kt @@ -5,18 +5,15 @@ import com.intellij.psi.PsiElement import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.psi.KtFile -import org.jetbrains.kotlin.psi2ir.PsiSourceManager class Psi2Ir(private val logger: FileLogger) : Psi2IrFacade { - companion object { - val psiManager = PsiSourceManager() - } - override fun getKtFile(irFile: IrFile): KtFile? { - return psiManager.getKtFile(irFile) + logger.warn("Comment extraction is not supported for Kotlin < 1.5.20") + return null } override fun findPsiElement(irElement: IrElement, irFile: IrFile): PsiElement? { - return psiManager.findPsiElement(irElement, irFile) + logger.error("Attempted comment extraction for Kotlin < 1.5.20") + return null } } diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_5_10/Psi2Ir.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_5_10/Psi2Ir.kt index 392e7cf6c28..fce55c87cd6 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_5_10/Psi2Ir.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_5_10/Psi2Ir.kt @@ -5,18 +5,15 @@ import com.intellij.psi.PsiElement import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.psi.KtFile -import org.jetbrains.kotlin.psi2ir.PsiSourceManager class Psi2Ir(private val logger: FileLogger) : Psi2IrFacade { - companion object { - val psiManager = PsiSourceManager() - } - override fun getKtFile(irFile: IrFile): KtFile? { - return psiManager.getKtFile(irFile) + logger.warn("Comment extraction is not supported for Kotlin < 1.5.20") + return null } override fun findPsiElement(irElement: IrElement, irFile: IrFile): PsiElement? { - return psiManager.findPsiElement(irElement, irFile) + logger.error("Attempted comment extraction for Kotlin < 1.5.20") + return null } } From 7fc9ae6c49eb9e7c7af20c6ddc0a907758422cfc Mon Sep 17 00:00:00 2001 From: Raul Garcia <42392023+raulgarciamsft@users.noreply.github.com> Date: Mon, 11 Jul 2022 13:07:20 -0700 Subject: [PATCH 179/505] Update python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql Co-authored-by: Taus --- .../CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql b/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql index 8db450a5ecb..df8182f5545 100644 --- a/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql +++ b/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql @@ -1,6 +1,6 @@ /** - * @name Unsafe usage of v1 version of Azure Storage client-side encryption (CVE-2022-PENDING). - * @description Unsafe usage of v1 version of Azure Storage client-side encryption, please refer to http://aka.ms/azstorageclientencryptionblog + * @name Unsafe usage of v1 version of Azure Storage client-side encryption. + * @description Using version v1 of Azure Storage client-side encryption is insecure, and may enable an attacker to decrypt encrypted data * @kind problem * @tags security * cryptography From e5702d0e1596c1d6b59bdca7d228c886942bf744 Mon Sep 17 00:00:00 2001 From: Raul Garcia <42392023+raulgarciamsft@users.noreply.github.com> Date: Mon, 11 Jul 2022 13:07:37 -0700 Subject: [PATCH 180/505] Update python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql Co-authored-by: Taus --- .../CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql b/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql index df8182f5545..6a489bf6774 100644 --- a/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql +++ b/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql @@ -5,7 +5,7 @@ * @tags security * cryptography * external/cwe/cwe-327 - * @id python/azure-storage/unsafe-client-side-encryption-in-use + * @id py/azure-storage/unsafe-client-side-encryption-in-use * @problem.severity error * @precision medium */ From ac055779669ffad52d5229f138e31487c88a4b0e Mon Sep 17 00:00:00 2001 From: Raul Garcia Date: Mon, 11 Jul 2022 13:25:35 -0700 Subject: [PATCH 181/505] Making various changes based on the feedback. Pending: 2 non-trivial fixes for Java & Python. --- ...nsafeUsageOfClientSideEncryptionVersion.cs | 11 ++++++++ ...feUsageOfClientSideEncryptionVersion.qhelp | 3 +++ ...nsafeUsageOfClientSideEncryptionVersion.ql | 26 ++++++++++++------- ...feUsageOfClientSideEncryptionVersion.qhelp | 8 +++--- ...nsafeUsageOfClientSideEncryptionVersion.ql | 11 ++++---- ...nsafeUsageOfClientSideEncryptionVersion.py | 12 ++++----- ...feUsageOfClientSideEncryptionVersion.qhelp | 8 +++--- ...nsafeUsageOfClientSideEncryptionVersion.ql | 11 +++----- 8 files changed, 51 insertions(+), 39 deletions(-) diff --git a/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.cs b/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.cs index 39928d9e2c7..bee97118ea8 100644 --- a/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.cs +++ b/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.cs @@ -1,4 +1,15 @@ +{ + SymmetricKey aesKey = new SymmetricKey(kid: "symencryptionkey"); + + // BAD: Using the outdated client side encryption version V1_0 + BlobEncryptionPolicy uploadPolicy = new BlobEncryptionPolicy(key: aesKey, keyResolver: null); + BlobRequestOptions uploadOptions = new BlobRequestOptions() { EncryptionPolicy = uploadPolicy }; + + MemoryStream stream = new MemoryStream(buffer); + blob.UploadFromStream(stream, length: size, accessCondition: null, options: uploadOptions); +} + var client = new BlobClient(myConnectionString, new SpecializedBlobClientOptions() { // BAD: Using an outdated SDK that does not support client side encryption version V2_0 diff --git a/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp b/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp index a3a33691854..e0ee017cb14 100644 --- a/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp +++ b/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp @@ -21,6 +21,9 @@
  • Azure Storage Client Encryption Blog.
  • +
  • + CVE-2022-PENDING +
  • diff --git a/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql b/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql index 095c154e9a3..b150ca71060 100644 --- a/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql +++ b/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql @@ -33,16 +33,25 @@ predicate isCreatingOutdatedAzureClientSideEncryptionObject(ObjectCreation oc, C } /** - * Holds if the Azure.Storage assembly for `c` is a version knwon to support - * version 2+ for client-side encryption and if the argument for the constructor `version` - * is set to a secure value. + * Holds if the Azure.Storage assembly for `c` is a version known to support + * version 2+ for client-side encryption */ -predicate isObjectCreationSafe(Expr versionExpr, Assembly asm) { - // Check if the Azure.Storage assembly version has the fix +predicate doesAzureStorageAssemblySupportSafeClientSideEncryption(Assembly asm) { exists(int versionCompare | versionCompare = asm.getVersion().compareTo("12.12.0.0") and versionCompare >= 0 ) and + asm.getName() = "Azure.Storage.Common" +} + +/** + * Holds if the Azure.Storage assembly for `c` is a version known to support + * version 2+ for client-side encryption and if the argument for the constructor `version` + * is set to a secure value. + */ +predicate isObjectCreationArgumentSafeAndUsingSafeVersionOfAssembly(Expr versionExpr, Assembly asm) { + // Check if the Azure.Storage assembly version has the fix + doesAzureStorageAssemblySupportSafeClientSideEncryption(asm) and // and that the version argument for the constructor is guaranteed to be Version2 isExprAnAccessToSafeClientSideEncryptionVersionValue(versionExpr) } @@ -56,8 +65,6 @@ predicate isExprAnAccessToSafeClientSideEncryptionVersionValue(Expr e) { ec.hasQualifiedName("Azure.Storage.ClientSideEncryptionVersion.V2_0") and ec.getAnAccess() = e ) - or - e.getValue().toInt() >= 2 } from Expr e, Class c, Assembly asm @@ -66,10 +73,9 @@ where ( exists(Expr e2 | isCreatingAzureClientSideEncryptionObject(e, c, e2) and - not isObjectCreationSafe(e2, asm) + not isObjectCreationArgumentSafeAndUsingSafeVersionOfAssembly(e2, asm) ) or isCreatingOutdatedAzureClientSideEncryptionObject(e, c) ) -select e, - "Unsafe usage of v1 version of Azure Storage client-side encryption (CVE-2022-PENDING). See http://aka.ms/azstorageclientencryptionblog" +select e, "Unsafe usage of v1 version of Azure Storage client-side encryption." diff --git a/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp b/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp index 3fca30a7926..45d919ec702 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp +++ b/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp @@ -14,11 +14,6 @@ -

    The following example shows an HTTP request parameter being used directly in a forming a -new request without validating the input, which facilitates SSRF attacks. -It also shows how to remedy the problem by validating the user input against a known fixed string. -

    -
    @@ -26,6 +21,9 @@ It also shows how to remedy the problem by validating the user input against a k
  • Azure Storage Client Encryption Blog.
  • +
  • + CVE-2022-PENDING +
  • diff --git a/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql b/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql index a109e945343..97bccf67314 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql @@ -13,7 +13,7 @@ import java /** - * Holds if the call `call` is an object creation for a class `EncryptedBlobClientBuilder` + * Holds if the call `call` is an object creation for a class `EncryptedBlobClientBuilder` * that takes no arguments, which means that it is using V1 encryption */ predicate isCreatingOutdatedAzureClientSideEncryptionObject(Call call, Class c) { @@ -31,8 +31,8 @@ predicate isCreatingOutdatedAzureClientSideEncryptionObject(Call call, Class c) ) } -/** - * Holds if the call `call` is an object creation for a class `EncryptedBlobClientBuilder` +/** + * Holds if the call `call` is an object creation for a class `EncryptedBlobClientBuilder` * that takes `versionArg` as the argument for the version. */ predicate isCreatingAzureClientSideEncryptionObjectNewVersion(Call call, Class c, Expr versionArg) { @@ -47,7 +47,7 @@ predicate isCreatingAzureClientSideEncryptionObjectNewVersion(Call call, Class c } /** - * Holds if the call `call` is an object creation for a class `EncryptedBlobClientBuilder` + * Holds if the call `call` is an object creation for a class `EncryptedBlobClientBuilder` * that takes `versionArg` as the argument for the version, and the version number is safe */ predicate isCreatingSafeAzureClientSideEncryptionObject(Call call, Class c, Expr versionArg) { @@ -67,5 +67,4 @@ where ) or isCreatingOutdatedAzureClientSideEncryptionObject(e, c) -select e, - "Unsafe usage of v1 version of Azure Storage client-side encryption (CVE-2022-PENDING). See http://aka.ms/azstorageclientencryptionblog" +select e, "Unsafe usage of v1 version of Azure Storage client-side encryption." diff --git a/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.py b/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.py index c4a0519f29d..b7099b3d6c0 100644 --- a/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.py +++ b/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.py @@ -1,7 +1,7 @@ blob_client = blob_service_client.get_blob_client(container=container_name, blob=blob_name) - blob_client.require_encryption = True - blob_client.key_encryption_key = kek - # GOOD: Must use `encryption_version` set to `2.0` - blob_client.encryption_version = '2.0' # Use Version 2.0! - with open(“decryptedcontentfile.txtâ€, “rbâ€) as stream: - blob_client.upload_blob(stream, overwrite=OVERWRITE_EXISTING) \ No newline at end of file +blob_client.require_encryption = True +blob_client.key_encryption_key = kek +# GOOD: Must use `encryption_version` set to `2.0` +blob_client.encryption_version = '2.0' # Use Version 2.0! +with open(“decryptedcontentfile.txtâ€, “rbâ€) as stream: + blob_client.upload_blob(stream, overwrite=OVERWRITE_EXISTING) \ No newline at end of file diff --git a/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp b/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp index c4111a166de..eaf49f371e6 100644 --- a/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp +++ b/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp @@ -14,11 +14,6 @@ -

    The following example shows an HTTP request parameter being used directly in a forming a -new request without validating the input, which facilitates SSRF attacks. -It also shows how to remedy the problem by validating the user input against a known fixed string. -

    -
    @@ -26,6 +21,9 @@ It also shows how to remedy the problem by validating the user input against a k
  • Azure Storage Client Encryption Blog.
  • +
  • + CVE-2022-PENDING +
  • diff --git a/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql b/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql index 6a489bf6774..55f695ca061 100644 --- a/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql +++ b/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql @@ -24,7 +24,7 @@ predicate isUnsafeClientSideAzureStorageEncryptionViaAttributes(Call call, AttrN not astmt.getValue() instanceof None and not exists(AssignStmt astmt2, Attribute a2, AttrNode encryptionVersionSet, StrConst uc | uc = astmt2.getValue() and - uc.getLiteralValue().toString() in ["'2.0'", "2.0"] and + uc.getText() in ["'2.0'", "2.0"] and a2.getAttr() = "encryption_version" and a2.getAFlowNode() = encryptionVersionSet and encryptionVersionSet.strictlyReaches(ctrlFlowNode) @@ -34,15 +34,13 @@ predicate isUnsafeClientSideAzureStorageEncryptionViaAttributes(Call call, AttrN predicate isUnsafeClientSideAzureStorageEncryptionViaObjectCreation(Call call, ControlFlowNode node) { exists(Keyword k | k.getAFlowNode() = node | - call.getFunc().(Name).getId().toString() in [ - "ContainerClient", "BlobClient", "BlobServiceClient" - ] and + call.getFunc().(Name).getId() in ["ContainerClient", "BlobClient", "BlobServiceClient"] and k.getArg() = "key_encryption_key" and k = call.getANamedArg() and not k.getValue() instanceof None and not exists(Keyword k2 | k2 = call.getANamedArg() | k2.getArg() = "encryption_version" and - k2.getValue().(StrConst).getLiteralValue().toString() in ["'2.0'", "2.0"] + k2.getValue().(StrConst).getText() in ["'2.0'", "2.0"] ) ) } @@ -51,5 +49,4 @@ from Call call, ControlFlowNode node where isUnsafeClientSideAzureStorageEncryptionViaAttributes(call, node) or isUnsafeClientSideAzureStorageEncryptionViaObjectCreation(call, node) -select node, - "Unsafe usage of v1 version of Azure Storage client-side encryption (CVE-2022-PENDING). See http://aka.ms/azstorageclientencryptionblog" +select node, "Unsafe usage of v1 version of Azure Storage client-side encryption." From 02e11b7ee92d78b7451a3e70e79c147da1a33524 Mon Sep 17 00:00:00 2001 From: Aditya Sharad Date: Mon, 11 Jul 2022 13:59:38 -0700 Subject: [PATCH 182/505] Docs: Add links from query help to query pack changelog for each language --- docs/codeql/query-help/cpp.rst | 4 +++- docs/codeql/query-help/csharp.rst | 4 +++- docs/codeql/query-help/go.rst | 4 +++- docs/codeql/query-help/java.rst | 4 +++- docs/codeql/query-help/javascript.rst | 4 +++- docs/codeql/query-help/python.rst | 4 +++- docs/codeql/query-help/ruby.rst | 4 +++- 7 files changed, 21 insertions(+), 7 deletions(-) diff --git a/docs/codeql/query-help/cpp.rst b/docs/codeql/query-help/cpp.rst index 7c3cbe304d7..12d2dfbf53e 100644 --- a/docs/codeql/query-help/cpp.rst +++ b/docs/codeql/query-help/cpp.rst @@ -3,7 +3,9 @@ CodeQL query help for C and C++ .. include:: ../reusables/query-help-overview.rst -For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository `__. +These queries are published in the CodeQL query pack ``codeql/cpp-queries`` (`changelog `__, `source `__). + +For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository `__. .. include:: toc-cpp.rst \ No newline at end of file diff --git a/docs/codeql/query-help/csharp.rst b/docs/codeql/query-help/csharp.rst index 9c5c6351ce3..5d7f732a588 100644 --- a/docs/codeql/query-help/csharp.rst +++ b/docs/codeql/query-help/csharp.rst @@ -3,6 +3,8 @@ CodeQL query help for C# .. include:: ../reusables/query-help-overview.rst -For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository `__. +These queries are published in the CodeQL query pack ``codeql/csharp-queries`` (`changelog `__, `source `__). + +For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository `__. .. include:: toc-csharp.rst \ No newline at end of file diff --git a/docs/codeql/query-help/go.rst b/docs/codeql/query-help/go.rst index 9e3050f74d0..bcd4aba9b51 100644 --- a/docs/codeql/query-help/go.rst +++ b/docs/codeql/query-help/go.rst @@ -3,6 +3,8 @@ CodeQL query help for Go .. include:: ../reusables/query-help-overview.rst -For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository `__. +These queries are published in the CodeQL query pack ``codeql/go-queries`` (`changelog `__, `source `__). + +For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository `__. .. include:: toc-go.rst diff --git a/docs/codeql/query-help/java.rst b/docs/codeql/query-help/java.rst index 8af2ee52890..4876546d2dc 100644 --- a/docs/codeql/query-help/java.rst +++ b/docs/codeql/query-help/java.rst @@ -3,6 +3,8 @@ CodeQL query help for Java .. include:: ../reusables/query-help-overview.rst -For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository `__. +These queries are published in the CodeQL query pack ``codeql/java-queries`` (`changelog `__, `source `__). + +For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository `__. .. include:: toc-java.rst diff --git a/docs/codeql/query-help/javascript.rst b/docs/codeql/query-help/javascript.rst index d7cf6797852..ae85de99f7b 100644 --- a/docs/codeql/query-help/javascript.rst +++ b/docs/codeql/query-help/javascript.rst @@ -3,6 +3,8 @@ CodeQL query help for JavaScript .. include:: ../reusables/query-help-overview.rst -For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository `__. +These queries are published in the CodeQL query pack ``codeql/javascript-queries`` (`changelog `__, `source `__). + +For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository `__. .. include:: toc-javascript.rst \ No newline at end of file diff --git a/docs/codeql/query-help/python.rst b/docs/codeql/query-help/python.rst index da68c1caa9b..9d915d443f3 100644 --- a/docs/codeql/query-help/python.rst +++ b/docs/codeql/query-help/python.rst @@ -3,6 +3,8 @@ CodeQL query help for Python .. include:: ../reusables/query-help-overview.rst -For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository `__. +These queries are published in the CodeQL query pack ``codeql/python-queries`` (`changelog `__, `source `__). + +For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository `__. .. include:: toc-python.rst \ No newline at end of file diff --git a/docs/codeql/query-help/ruby.rst b/docs/codeql/query-help/ruby.rst index 3ce1304ba76..e733aecaf79 100644 --- a/docs/codeql/query-help/ruby.rst +++ b/docs/codeql/query-help/ruby.rst @@ -3,6 +3,8 @@ CodeQL query help for Ruby .. include:: ../reusables/query-help-overview.rst -For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository `__. +These queries are published in the CodeQL query pack ``codeql/ruby-queries`` (`changelog `__, `source `__). + +For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository `__. .. include:: toc-ruby.rst From d5791e2d56e8dfd348747f09e23f69596f5c7619 Mon Sep 17 00:00:00 2001 From: Raul Garcia Date: Mon, 11 Jul 2022 15:45:15 -0700 Subject: [PATCH 183/505] Addressing feedback from the PR --- ...nsafeUsageOfClientSideEncryptionVersion.ql | 30 ++++++++++++++++--- ...nsafeUsageOfClientSideEncryptionVersion.ql | 7 +++-- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql b/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql index 97bccf67314..50840b67f0b 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql @@ -11,6 +11,7 @@ */ import java +import semmle.code.java.dataflow.DataFlow /** * Holds if the call `call` is an object creation for a class `EncryptedBlobClientBuilder` @@ -46,16 +47,37 @@ predicate isCreatingAzureClientSideEncryptionObjectNewVersion(Call call, Class c ) } +/** + * A config that tracks `EncryptedBlobClientBuilder.version` argument initialization. + */ +private class EncryptedBlobClientBuilderEncryptionVersionConfig extends DataFlow::Configuration { + EncryptedBlobClientBuilderEncryptionVersionConfig() { + this = "EncryptedBlobClientBuilderEncryptionVersionConfig" + } + + override predicate isSource(DataFlow::Node source) { + exists(FieldRead fr, Field f | fr = source.asExpr() | + f.getAnAccess() = fr and + f.hasQualifiedName("com.azure.storage.blob.specialized.cryptography", "EncryptionVersion", + "V2") + ) + } + + override predicate isSink(DataFlow::Node sink) { + isCreatingAzureClientSideEncryptionObjectNewVersion(_, _, sink.asExpr()) + } +} + /** * Holds if the call `call` is an object creation for a class `EncryptedBlobClientBuilder` * that takes `versionArg` as the argument for the version, and the version number is safe */ predicate isCreatingSafeAzureClientSideEncryptionObject(Call call, Class c, Expr versionArg) { isCreatingAzureClientSideEncryptionObjectNewVersion(call, c, versionArg) and - exists(FieldRead fr, Field f | - fr = versionArg and - f.getAnAccess() = fr and - f.hasQualifiedName("com.azure.storage.blob.specialized.cryptography", "EncryptionVersion", "V2") + exists(EncryptedBlobClientBuilderEncryptionVersionConfig config, DataFlow::Node sink | + sink.asExpr() = versionArg + | + config.hasFlow(_, sink) ) } diff --git a/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql b/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql index 55f695ca061..442399e658f 100644 --- a/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql +++ b/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql @@ -11,6 +11,7 @@ */ import python +import semmle.python.ApiGraphs predicate isUnsafeClientSideAzureStorageEncryptionViaAttributes(Call call, AttrNode node) { exists(ControlFlowNode ctrlFlowNode, AssignStmt astmt, Attribute a | @@ -33,8 +34,10 @@ predicate isUnsafeClientSideAzureStorageEncryptionViaAttributes(Call call, AttrN } predicate isUnsafeClientSideAzureStorageEncryptionViaObjectCreation(Call call, ControlFlowNode node) { - exists(Keyword k | k.getAFlowNode() = node | - call.getFunc().(Name).getId() in ["ContainerClient", "BlobClient", "BlobServiceClient"] and + exists(API::Node c, string s, Keyword k | k.getAFlowNode() = node | + c.getACall().asExpr() = call and + c = API::moduleImport("azure").getMember("storage").getMember("blob").getMember(s) and + s in ["ContainerClient", "BlobClient", "BlobServiceClient"] and k.getArg() = "key_encryption_key" and k = call.getANamedArg() and not k.getValue() instanceof None and From 217c9a8aaf680e47b66031a88b3ac04e6c7a85e2 Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Tue, 12 Jul 2022 08:50:58 +0100 Subject: [PATCH 184/505] Fix typo in changenote Co-authored-by: intrigus-lgtm <60750685+intrigus-lgtm@users.noreply.github.com> --- ruby/ql/lib/change-notes/2022-07-11-command-execution.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruby/ql/lib/change-notes/2022-07-11-command-execution.md b/ruby/ql/lib/change-notes/2022-07-11-command-execution.md index 2c5addba1f5..f6548719b57 100644 --- a/ruby/ql/lib/change-notes/2022-07-11-command-execution.md +++ b/ruby/ql/lib/change-notes/2022-07-11-command-execution.md @@ -1,6 +1,6 @@ --- category: minorAnalysis --- -* Fixed a bug causing every expression in the database to be a considered a system-command execution sink when calls to any of the following methods exist: +* Fixed a bug causing every expression in the database to be considered a system-command execution sink when calls to any of the following methods exist: * The `spawn`, `fspawn`, `popen4`, `pspawn`, `system`, `_pspawn` methods and the backtick operator from the `POSIX::spawn` gem. * The `execute_command`, `rake`, `rails_command`, and `git` methods in `Rails::Generation::Actions`. From c75599c3da63e8a2f99319dfa29131bdec9ab396 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Tue, 12 Jul 2022 10:27:22 +0200 Subject: [PATCH 185/505] C++: Clarify the "most-specific" part of `FunctionCall:getTarget` --- cpp/ql/lib/semmle/code/cpp/exprs/Call.qll | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/exprs/Call.qll b/cpp/ql/lib/semmle/code/cpp/exprs/Call.qll index 7ceda8ddbff..dba3d16997f 100644 --- a/cpp/ql/lib/semmle/code/cpp/exprs/Call.qll +++ b/cpp/ql/lib/semmle/code/cpp/exprs/Call.qll @@ -255,8 +255,10 @@ class FunctionCall extends Call, @funbindexpr { /** * Gets the function called by this call. * - * In the case of virtual function calls, the result is the most-specific function in the override tree (as - * determined by the compiler) such that the target at runtime will be one of `result.getAnOverridingFunction*()`. + * In the case of virtual function calls, the result is the most-specific function in the override tree + * such that the target at runtime will be one of `result.getAnOverridingFunction*()`. The most-specific + * function is determined by the compiler based on the compile time type of the object the function is a + * member of. */ override Function getTarget() { funbind(underlyingElement(this), unresolveElement(result)) } From 033b239b22cb3ccabb988b9f9587ce8e59826895 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Tue, 12 Jul 2022 10:33:54 +0200 Subject: [PATCH 186/505] Swift: collapse `TypeRepr` hierarchy Now `TypeRepr` is a final class in the AST, which is more or less just a type with a location in code. As the frontend does not provide a direct way to get a type from a type representation, this information must be provided when fetching the label of a type repr. This meant: * removing the type repr field from `EnumIsCaseExpr`: this is a virtual AST node introduced in place of some kinds of `IsEpxr`. The type repr is still available from the `ConditionalCheckedCastExpr` wrapped by this virtual node, and we will rebuild the original `IsExpr` with the IPA layer. * some logic to get the type of keypath roots has been added to `KeyPathExpr`. This was done to keep the `TypeRepr` to `Type` relation total in the DB, but goes against the design of a dumb extractor. The logic could be moved to QL in the future * in the control flow library, `TypeRepr` children are now ignored. As far as I can tell, there is no runtime evaluation going on in `TypeRepr`s, so it does not make much sense to have control flow through them. --- swift/codegen/schema.yml | 91 +--- swift/extractor/infra/SwiftDispatcher.h | 22 +- swift/extractor/infra/SwiftTagTraits.h | 4 - swift/extractor/trap/BUILD.bazel | 2 +- swift/extractor/visitors/ExprVisitor.cpp | 18 +- swift/extractor/visitors/PatternVisitor.cpp | 7 +- swift/extractor/visitors/SwiftVisitor.h | 6 +- swift/extractor/visitors/TypeReprVisitor.cpp | 3 - swift/extractor/visitors/TypeReprVisitor.h | 13 - swift/extractor/visitors/TypeVisitor.cpp | 6 + swift/extractor/visitors/TypeVisitor.h | 2 + .../internal/ControlFlowGraphImpl.qll | 15 +- swift/ql/lib/codeql/swift/elements.qll | 30 +- .../codeql/swift/elements/type/TypeRepr.qll | 5 + .../typerepr/ComponentIdentTypeRepr.qll | 4 - .../elements/typerepr/CompositionTypeRepr.qll | 4 - .../swift/elements/typerepr/ErrorTypeRepr.qll | 4 - .../elements/typerepr/ExistentialTypeRepr.qll | 4 - .../swift/elements/typerepr/FixedTypeRepr.qll | 4 - .../swift/elements/typerepr/IdentTypeRepr.qll | 4 - .../typerepr/NamedOpaqueReturnTypeRepr.qll | 4 - .../typerepr/OpaqueReturnTypeRepr.qll | 4 - .../elements/typerepr/PlaceholderTypeRepr.qll | 4 - .../elements/typerepr/SimpleIdentTypeRepr.qll | 4 - .../elements/typerepr/SpecifierTypeRepr.qll | 4 - .../swift/elements/typerepr/TypeRepr.qll | 4 - .../swift/generated/GetImmediateParent.qll | 8 +- .../swift/generated/expr/EnumIsCaseExpr.qll | 12 +- .../swift/generated/expr/KeyPathExpr.qll | 9 +- .../codeql/swift/generated/expr/TypeExpr.qll | 2 +- .../swift/generated/pattern/IsPattern.qll | 2 +- .../swift/generated/pattern/TypedPattern.qll | 2 +- .../codeql/swift/generated/type/TypeRepr.qll | 14 + .../generated/typerepr/ArrayTypeRepr.qll | 6 - .../generated/typerepr/AttributedTypeRepr.qll | 6 - .../typerepr/CompileTimeConstTypeRepr.qll | 6 - .../typerepr/ComponentIdentTypeRepr.qll | 4 - .../typerepr/CompositionTypeRepr.qll | 6 - .../typerepr/CompoundIdentTypeRepr.qll | 6 - .../generated/typerepr/DictionaryTypeRepr.qll | 6 - .../generated/typerepr/ErrorTypeRepr.qll | 6 - .../typerepr/ExistentialTypeRepr.qll | 6 - .../generated/typerepr/FixedTypeRepr.qll | 6 - .../generated/typerepr/FunctionTypeRepr.qll | 6 - .../typerepr/GenericIdentTypeRepr.qll | 6 - .../generated/typerepr/IdentTypeRepr.qll | 4 - .../ImplicitlyUnwrappedOptionalTypeRepr.qll | 7 - .../generated/typerepr/InOutTypeRepr.qll | 6 - .../generated/typerepr/IsolatedTypeRepr.qll | 6 - .../generated/typerepr/MetatypeTypeRepr.qll | 6 - .../typerepr/NamedOpaqueReturnTypeRepr.qll | 6 - .../typerepr/OpaqueReturnTypeRepr.qll | 6 - .../generated/typerepr/OptionalTypeRepr.qll | 6 - .../generated/typerepr/OwnedTypeRepr.qll | 6 - .../typerepr/PlaceholderTypeRepr.qll | 6 - .../generated/typerepr/ProtocolTypeRepr.qll | 6 - .../generated/typerepr/SharedTypeRepr.qll | 6 - .../generated/typerepr/SilBoxTypeRepr.qll | 6 - .../typerepr/SimpleIdentTypeRepr.qll | 6 - .../generated/typerepr/SpecifierTypeRepr.qll | 4 - .../generated/typerepr/TupleTypeRepr.qll | 6 - .../swift/generated/typerepr/TypeRepr.qll | 4 - swift/ql/lib/swift.dbscheme | 148 +----- .../extractor-tests/expressions/all.expected | 4 - .../EnumIsCaseExpr/EnumIsCaseExpr.expected | 20 +- .../expr/EnumIsCaseExpr/EnumIsCaseExpr.ql | 5 +- .../UnresolvedDotExpr}/MISSING_SOURCE.txt | 0 .../UnresolvedDotExpr.expected | 3 - .../UnresolvedDotExpr/UnresolvedDotExpr.ql | 11 - .../UnresolvedDotExpr_getType.expected | 0 .../UnresolvedDotExpr_getType.ql | 7 - .../unresolved_dot_expr.swift | 11 - .../TypeRepr}/MISSING_SOURCE.txt | 0 .../MISSING_SOURCE.txt | 4 - .../CompositionTypeRepr/MISSING_SOURCE.txt | 4 - .../CompoundIdentTypeRepr/MISSING_SOURCE.txt | 4 - .../DictionaryTypeRepr/MISSING_SOURCE.txt | 4 - .../typerepr/ErrorTypeRepr/MISSING_SOURCE.txt | 4 - .../ExistentialTypeRepr/MISSING_SOURCE.txt | 4 - .../typerepr/FixedTypeRepr/MISSING_SOURCE.txt | 4 - .../FunctionTypeRepr/MISSING_SOURCE.txt | 4 - .../GenericIdentTypeRepr/MISSING_SOURCE.txt | 4 - .../MISSING_SOURCE.txt | 4 - .../typerepr/InOutTypeRepr/MISSING_SOURCE.txt | 4 - .../IsolatedTypeRepr/MISSING_SOURCE.txt | 4 - .../MetatypeTypeRepr/MISSING_SOURCE.txt | 4 - .../MISSING_SOURCE.txt | 4 - .../OpaqueReturnTypeRepr/MISSING_SOURCE.txt | 4 - .../OptionalTypeRepr/MISSING_SOURCE.txt | 4 - .../typerepr/OwnedTypeRepr/MISSING_SOURCE.txt | 4 - .../PlaceholderTypeRepr/MISSING_SOURCE.txt | 4 - .../ProtocolTypeRepr/MISSING_SOURCE.txt | 4 - .../SharedTypeRepr/MISSING_SOURCE.txt | 4 - .../SilBoxTypeRepr/MISSING_SOURCE.txt | 4 - .../SimpleIdentTypeRepr/MISSING_SOURCE.txt | 4 - .../typerepr/TupleTypeRepr/MISSING_SOURCE.txt | 4 - .../controlflow/graph/Cfg.expected | 481 +++++++++++++++++- .../test/library-tests/parent/parent.expected | 136 +++-- 98 files changed, 646 insertions(+), 760 deletions(-) delete mode 100644 swift/extractor/visitors/TypeReprVisitor.cpp delete mode 100644 swift/extractor/visitors/TypeReprVisitor.h create mode 100644 swift/ql/lib/codeql/swift/elements/type/TypeRepr.qll delete mode 100644 swift/ql/lib/codeql/swift/elements/typerepr/ComponentIdentTypeRepr.qll delete mode 100644 swift/ql/lib/codeql/swift/elements/typerepr/CompositionTypeRepr.qll delete mode 100644 swift/ql/lib/codeql/swift/elements/typerepr/ErrorTypeRepr.qll delete mode 100644 swift/ql/lib/codeql/swift/elements/typerepr/ExistentialTypeRepr.qll delete mode 100644 swift/ql/lib/codeql/swift/elements/typerepr/FixedTypeRepr.qll delete mode 100644 swift/ql/lib/codeql/swift/elements/typerepr/IdentTypeRepr.qll delete mode 100644 swift/ql/lib/codeql/swift/elements/typerepr/NamedOpaqueReturnTypeRepr.qll delete mode 100644 swift/ql/lib/codeql/swift/elements/typerepr/OpaqueReturnTypeRepr.qll delete mode 100644 swift/ql/lib/codeql/swift/elements/typerepr/PlaceholderTypeRepr.qll delete mode 100644 swift/ql/lib/codeql/swift/elements/typerepr/SimpleIdentTypeRepr.qll delete mode 100644 swift/ql/lib/codeql/swift/elements/typerepr/SpecifierTypeRepr.qll delete mode 100644 swift/ql/lib/codeql/swift/elements/typerepr/TypeRepr.qll create mode 100644 swift/ql/lib/codeql/swift/generated/type/TypeRepr.qll delete mode 100644 swift/ql/lib/codeql/swift/generated/typerepr/ArrayTypeRepr.qll delete mode 100644 swift/ql/lib/codeql/swift/generated/typerepr/AttributedTypeRepr.qll delete mode 100644 swift/ql/lib/codeql/swift/generated/typerepr/CompileTimeConstTypeRepr.qll delete mode 100644 swift/ql/lib/codeql/swift/generated/typerepr/ComponentIdentTypeRepr.qll delete mode 100644 swift/ql/lib/codeql/swift/generated/typerepr/CompositionTypeRepr.qll delete mode 100644 swift/ql/lib/codeql/swift/generated/typerepr/CompoundIdentTypeRepr.qll delete mode 100644 swift/ql/lib/codeql/swift/generated/typerepr/DictionaryTypeRepr.qll delete mode 100644 swift/ql/lib/codeql/swift/generated/typerepr/ErrorTypeRepr.qll delete mode 100644 swift/ql/lib/codeql/swift/generated/typerepr/ExistentialTypeRepr.qll delete mode 100644 swift/ql/lib/codeql/swift/generated/typerepr/FixedTypeRepr.qll delete mode 100644 swift/ql/lib/codeql/swift/generated/typerepr/FunctionTypeRepr.qll delete mode 100644 swift/ql/lib/codeql/swift/generated/typerepr/GenericIdentTypeRepr.qll delete mode 100644 swift/ql/lib/codeql/swift/generated/typerepr/IdentTypeRepr.qll delete mode 100644 swift/ql/lib/codeql/swift/generated/typerepr/ImplicitlyUnwrappedOptionalTypeRepr.qll delete mode 100644 swift/ql/lib/codeql/swift/generated/typerepr/InOutTypeRepr.qll delete mode 100644 swift/ql/lib/codeql/swift/generated/typerepr/IsolatedTypeRepr.qll delete mode 100644 swift/ql/lib/codeql/swift/generated/typerepr/MetatypeTypeRepr.qll delete mode 100644 swift/ql/lib/codeql/swift/generated/typerepr/NamedOpaqueReturnTypeRepr.qll delete mode 100644 swift/ql/lib/codeql/swift/generated/typerepr/OpaqueReturnTypeRepr.qll delete mode 100644 swift/ql/lib/codeql/swift/generated/typerepr/OptionalTypeRepr.qll delete mode 100644 swift/ql/lib/codeql/swift/generated/typerepr/OwnedTypeRepr.qll delete mode 100644 swift/ql/lib/codeql/swift/generated/typerepr/PlaceholderTypeRepr.qll delete mode 100644 swift/ql/lib/codeql/swift/generated/typerepr/ProtocolTypeRepr.qll delete mode 100644 swift/ql/lib/codeql/swift/generated/typerepr/SharedTypeRepr.qll delete mode 100644 swift/ql/lib/codeql/swift/generated/typerepr/SilBoxTypeRepr.qll delete mode 100644 swift/ql/lib/codeql/swift/generated/typerepr/SimpleIdentTypeRepr.qll delete mode 100644 swift/ql/lib/codeql/swift/generated/typerepr/SpecifierTypeRepr.qll delete mode 100644 swift/ql/lib/codeql/swift/generated/typerepr/TupleTypeRepr.qll delete mode 100644 swift/ql/lib/codeql/swift/generated/typerepr/TypeRepr.qll rename swift/ql/test/extractor-tests/generated/{typerepr/ArrayTypeRepr => expr/UnresolvedDotExpr}/MISSING_SOURCE.txt (100%) delete mode 100644 swift/ql/test/extractor-tests/generated/expr/UnresolvedDotExpr/UnresolvedDotExpr.expected delete mode 100644 swift/ql/test/extractor-tests/generated/expr/UnresolvedDotExpr/UnresolvedDotExpr.ql delete mode 100644 swift/ql/test/extractor-tests/generated/expr/UnresolvedDotExpr/UnresolvedDotExpr_getType.expected delete mode 100644 swift/ql/test/extractor-tests/generated/expr/UnresolvedDotExpr/UnresolvedDotExpr_getType.ql delete mode 100644 swift/ql/test/extractor-tests/generated/expr/UnresolvedDotExpr/unresolved_dot_expr.swift rename swift/ql/test/extractor-tests/generated/{typerepr/AttributedTypeRepr => type/TypeRepr}/MISSING_SOURCE.txt (100%) delete mode 100644 swift/ql/test/extractor-tests/generated/typerepr/CompileTimeConstTypeRepr/MISSING_SOURCE.txt delete mode 100644 swift/ql/test/extractor-tests/generated/typerepr/CompositionTypeRepr/MISSING_SOURCE.txt delete mode 100644 swift/ql/test/extractor-tests/generated/typerepr/CompoundIdentTypeRepr/MISSING_SOURCE.txt delete mode 100644 swift/ql/test/extractor-tests/generated/typerepr/DictionaryTypeRepr/MISSING_SOURCE.txt delete mode 100644 swift/ql/test/extractor-tests/generated/typerepr/ErrorTypeRepr/MISSING_SOURCE.txt delete mode 100644 swift/ql/test/extractor-tests/generated/typerepr/ExistentialTypeRepr/MISSING_SOURCE.txt delete mode 100644 swift/ql/test/extractor-tests/generated/typerepr/FixedTypeRepr/MISSING_SOURCE.txt delete mode 100644 swift/ql/test/extractor-tests/generated/typerepr/FunctionTypeRepr/MISSING_SOURCE.txt delete mode 100644 swift/ql/test/extractor-tests/generated/typerepr/GenericIdentTypeRepr/MISSING_SOURCE.txt delete mode 100644 swift/ql/test/extractor-tests/generated/typerepr/ImplicitlyUnwrappedOptionalTypeRepr/MISSING_SOURCE.txt delete mode 100644 swift/ql/test/extractor-tests/generated/typerepr/InOutTypeRepr/MISSING_SOURCE.txt delete mode 100644 swift/ql/test/extractor-tests/generated/typerepr/IsolatedTypeRepr/MISSING_SOURCE.txt delete mode 100644 swift/ql/test/extractor-tests/generated/typerepr/MetatypeTypeRepr/MISSING_SOURCE.txt delete mode 100644 swift/ql/test/extractor-tests/generated/typerepr/NamedOpaqueReturnTypeRepr/MISSING_SOURCE.txt delete mode 100644 swift/ql/test/extractor-tests/generated/typerepr/OpaqueReturnTypeRepr/MISSING_SOURCE.txt delete mode 100644 swift/ql/test/extractor-tests/generated/typerepr/OptionalTypeRepr/MISSING_SOURCE.txt delete mode 100644 swift/ql/test/extractor-tests/generated/typerepr/OwnedTypeRepr/MISSING_SOURCE.txt delete mode 100644 swift/ql/test/extractor-tests/generated/typerepr/PlaceholderTypeRepr/MISSING_SOURCE.txt delete mode 100644 swift/ql/test/extractor-tests/generated/typerepr/ProtocolTypeRepr/MISSING_SOURCE.txt delete mode 100644 swift/ql/test/extractor-tests/generated/typerepr/SharedTypeRepr/MISSING_SOURCE.txt delete mode 100644 swift/ql/test/extractor-tests/generated/typerepr/SilBoxTypeRepr/MISSING_SOURCE.txt delete mode 100644 swift/ql/test/extractor-tests/generated/typerepr/SimpleIdentTypeRepr/MISSING_SOURCE.txt delete mode 100644 swift/ql/test/extractor-tests/generated/typerepr/TupleTypeRepr/MISSING_SOURCE.txt diff --git a/swift/codegen/schema.yml b/swift/codegen/schema.yml index 01bb0c2feba..7d6d530170d 100644 --- a/swift/codegen/schema.yml +++ b/swift/codegen/schema.yml @@ -7,8 +7,7 @@ _includes: _directories: decl: Decl$|Context$ pattern: Pattern$ - type: Type$ - typerepr: TypeRepr$ + type: Type(Repr)?$ expr: Expr$ stmt: Stmt$ @@ -181,6 +180,7 @@ Stmt: TypeRepr: _extends: AstNode + type: Type FunctionType: _extends: AnyFunctionType @@ -391,7 +391,6 @@ EnumIsCaseExpr: _extends: Expr _children: sub_expr: Expr - type_repr: TypeRepr element: EnumElementDecl ErrorExpr: @@ -442,7 +441,7 @@ KeyPathDotExpr: KeyPathExpr: _extends: Expr _children: - parsed_root: Expr? + root: TypeRepr? parsed_path: Expr? LazyInitializerExpr: @@ -1162,87 +1161,3 @@ FloatLiteralExpr: IntegerLiteralExpr: _extends: NumberLiteralExpr string_value: string - -ErrorTypeRepr: - _extends: TypeRepr - -AttributedTypeRepr: - _extends: TypeRepr - -IdentTypeRepr: - _extends: TypeRepr - -ComponentIdentTypeRepr: - _extends: IdentTypeRepr - -SimpleIdentTypeRepr: - _extends: ComponentIdentTypeRepr - -GenericIdentTypeRepr: - _extends: ComponentIdentTypeRepr - -CompoundIdentTypeRepr: - _extends: IdentTypeRepr - -FunctionTypeRepr: - _extends: TypeRepr - -ArrayTypeRepr: - _extends: TypeRepr - -DictionaryTypeRepr: - _extends: TypeRepr - -OptionalTypeRepr: - _extends: TypeRepr - -ImplicitlyUnwrappedOptionalTypeRepr: - _extends: TypeRepr - -TupleTypeRepr: - _extends: TypeRepr - -CompositionTypeRepr: - _extends: TypeRepr - -MetatypeTypeRepr: - _extends: TypeRepr - -ProtocolTypeRepr: - _extends: TypeRepr - -OpaqueReturnTypeRepr: - _extends: TypeRepr - -NamedOpaqueReturnTypeRepr: - _extends: TypeRepr - -ExistentialTypeRepr: - _extends: TypeRepr - -PlaceholderTypeRepr: - _extends: TypeRepr - -SpecifierTypeRepr: - _extends: TypeRepr - -InOutTypeRepr: - _extends: SpecifierTypeRepr - -SharedTypeRepr: - _extends: SpecifierTypeRepr - -OwnedTypeRepr: - _extends: SpecifierTypeRepr - -IsolatedTypeRepr: - _extends: SpecifierTypeRepr - -CompileTimeConstTypeRepr: - _extends: SpecifierTypeRepr - -FixedTypeRepr: - _extends: TypeRepr - -SilBoxTypeRepr: - _extends: TypeRepr diff --git a/swift/extractor/infra/SwiftDispatcher.h b/swift/extractor/infra/SwiftDispatcher.h index c7ca43a1614..0f1753cda1d 100644 --- a/swift/extractor/infra/SwiftDispatcher.h +++ b/swift/extractor/infra/SwiftDispatcher.h @@ -64,8 +64,8 @@ class SwiftDispatcher { // This method gives a TRAP label for already emitted AST node. // If the AST node was not emitted yet, then the emission is dispatched to a corresponding // visitor (see `visit(T *)` methods below). - template - TrapLabelOf fetchLabel(E* e) { + template + TrapLabelOf fetchLabel(E* e, Args&&... args) { assert(e && "trying to fetch a label on nullptr, maybe fetchOptionalLabel is to be used?"); // this is required so we avoid any recursive loop: a `fetchLabel` during the visit of `e` might // end up calling `fetchLabel` on `e` itself, so we want the visit of `e` to call `fetchLabel` @@ -76,7 +76,7 @@ class SwiftDispatcher { return *l; } waitingForNewLabel = e; - visit(e); + visit(e, std::forward(args)...); if (auto l = store.get(e)) { if constexpr (!std::is_base_of_v) { attachLocation(e, *l); @@ -158,10 +158,10 @@ class SwiftDispatcher { // return `std::optional(fetchLabel(arg))` if arg converts to true, otherwise std::nullopt // universal reference `Arg&&` is used to catch both temporary and non-const references, not // for perfect forwarding - template - auto fetchOptionalLabel(Arg&& arg) -> std::optional { + template + auto fetchOptionalLabel(Arg&& arg, Args&&... args) -> std::optional { if (arg) { - return fetchLabel(arg); + return fetchLabel(arg, std::forward(args)...); } return std::nullopt; } @@ -251,9 +251,11 @@ class SwiftDispatcher { template bool fetchLabelFromUnionCase(const llvm::PointerUnion u, TrapLabel& output) { - if (auto e = u.template dyn_cast()) { - output = fetchLabel(e); - return true; + if constexpr (!std::is_same_v) { + if (auto e = u.template dyn_cast()) { + output = fetchLabel(e); + return true; + } } return false; } @@ -279,7 +281,7 @@ class SwiftDispatcher { virtual void visit(swift::CaseLabelItem* item) = 0; virtual void visit(swift::Expr* expr) = 0; virtual void visit(swift::Pattern* pattern) = 0; - virtual void visit(swift::TypeRepr* type) = 0; + virtual void visit(swift::TypeRepr* typeRepr, swift::Type type) = 0; virtual void visit(swift::TypeBase* type) = 0; const swift::SourceManager& sourceManager; diff --git a/swift/extractor/infra/SwiftTagTraits.h b/swift/extractor/infra/SwiftTagTraits.h index 8a2e489683d..96da48813d3 100644 --- a/swift/extractor/infra/SwiftTagTraits.h +++ b/swift/extractor/infra/SwiftTagTraits.h @@ -14,7 +14,6 @@ using SILBlockStorageTypeTag = SilBlockStorageTypeTag; using SILBoxTypeTag = SilBoxTypeTag; using SILFunctionTypeTag = SilFunctionTypeTag; using SILTokenTypeTag = SilTokenTypeTag; -using SILBoxTypeReprTag = SilBoxTypeReprTag; #define MAP_TYPE_TO_TAG(TYPE, TAG) \ template <> \ @@ -57,9 +56,6 @@ MAP_TAG(Pattern); #include MAP_TAG(TypeRepr); -#define ABSTRACT_TYPEREPR(CLASS, PARENT) MAP_SUBTAG(CLASS##TypeRepr, PARENT) -#define TYPEREPR(CLASS, PARENT) ABSTRACT_TYPEREPR(CLASS, PARENT) -#include MAP_TYPE_TO_TAG(TypeBase, TypeTag); #define ABSTRACT_TYPE(CLASS, PARENT) MAP_SUBTAG(CLASS##Type, PARENT) diff --git a/swift/extractor/trap/BUILD.bazel b/swift/extractor/trap/BUILD.bazel index b1860142866..08875c45263 100644 --- a/swift/extractor/trap/BUILD.bazel +++ b/swift/extractor/trap/BUILD.bazel @@ -1,6 +1,6 @@ load("//swift:rules.bzl", "swift_cc_library") -_dirs = ("", "decl/", "expr/", "pattern/", "stmt/", "type/", "typerepr/") +_dirs = ("", "decl/", "expr/", "pattern/", "stmt/", "type/") genrule( name = "cppgen", diff --git a/swift/extractor/visitors/ExprVisitor.cpp b/swift/extractor/visitors/ExprVisitor.cpp index e6aabef4cea..8d07372d024 100644 --- a/swift/extractor/visitors/ExprVisitor.cpp +++ b/swift/extractor/visitors/ExprVisitor.cpp @@ -188,9 +188,8 @@ void ExprVisitor::visitEnumIsCaseExpr(swift::EnumIsCaseExpr* expr) { assert(expr->getCaseTypeRepr() && "EnumIsCaseExpr has CaseTypeRepr"); assert(expr->getEnumElement() && "EnumIsCaseExpr has EnumElement"); auto subExpr = dispatcher_.fetchLabel(expr->getSubExpr()); - auto typeRepr = dispatcher_.fetchLabel(expr->getCaseTypeRepr()); auto enumElement = dispatcher_.fetchLabel(expr->getEnumElement()); - dispatcher_.emit(EnumIsCaseExprsTrap{label, subExpr, typeRepr, enumElement}); + dispatcher_.emit(EnumIsCaseExprsTrap{label, subExpr, enumElement}); } void ExprVisitor::visitMakeTemporarilyEscapableExpr(swift::MakeTemporarilyEscapableExpr* expr) { @@ -288,7 +287,9 @@ void ExprVisitor::visitErasureExpr(swift::ErasureExpr* expr) { codeql::TypeExpr ExprVisitor::translateTypeExpr(const swift::TypeExpr& expr) { TypeExpr entry{dispatcher_.assignNewLabel(expr)}; - entry.type_repr = dispatcher_.fetchOptionalLabel(expr.getTypeRepr()); + if (expr.getTypeRepr() && expr.getInstanceType()) { + entry.type_repr = dispatcher_.fetchLabel(expr.getTypeRepr(), expr.getInstanceType()); + } return entry; } @@ -478,9 +479,14 @@ void ExprVisitor::visitKeyPathExpr(swift::KeyPathExpr* expr) { auto pathLabel = dispatcher_.fetchLabel(path); dispatcher_.emit(KeyPathExprParsedPathsTrap{label, pathLabel}); } - if (auto root = expr->getParsedRoot()) { - auto rootLabel = dispatcher_.fetchLabel(root); - dispatcher_.emit(KeyPathExprParsedRootsTrap{label, rootLabel}); + // TODO maybe move this logic to QL? + if (auto rootTypeRepr = expr->getRootType()) { + auto keyPathType = expr->getType()->getAs(); + assert(keyPathType && "KeyPathExpr must have BoundGenericClassType"); + auto keyPathTypeArgs = keyPathType->getGenericArgs(); + assert(keyPathTypeArgs.size() != 0 && "KeyPathExpr type must have generic args"); + auto rootLabel = dispatcher_.fetchLabel(rootTypeRepr, keyPathTypeArgs[0]); + dispatcher_.emit(KeyPathExprRootsTrap{label, rootLabel}); } } } diff --git a/swift/extractor/visitors/PatternVisitor.cpp b/swift/extractor/visitors/PatternVisitor.cpp index 43421fcbede..4ef90aa56a4 100644 --- a/swift/extractor/visitors/PatternVisitor.cpp +++ b/swift/extractor/visitors/PatternVisitor.cpp @@ -18,8 +18,8 @@ void PatternVisitor::visitTypedPattern(swift::TypedPattern* pattern) { assert(pattern->getSubPattern() && "expect TypedPattern to have a SubPattern"); dispatcher_.emit(TypedPatternsTrap{label, dispatcher_.fetchLabel(pattern->getSubPattern())}); if (auto typeRepr = pattern->getTypeRepr()) { - dispatcher_.emit( - TypedPatternTypeReprsTrap{label, dispatcher_.fetchLabel(pattern->getTypeRepr())}); + dispatcher_.emit(TypedPatternTypeReprsTrap{ + label, dispatcher_.fetchLabel(pattern->getTypeRepr(), pattern->getType())}); } } @@ -63,7 +63,8 @@ void PatternVisitor::visitIsPattern(swift::IsPattern* pattern) { dispatcher_.emit(IsPatternsTrap{label}); if (auto typeRepr = pattern->getCastTypeRepr()) { - dispatcher_.emit(IsPatternCastTypeReprsTrap{label, dispatcher_.fetchLabel(typeRepr)}); + dispatcher_.emit(IsPatternCastTypeReprsTrap{ + label, dispatcher_.fetchLabel(typeRepr, pattern->getCastType())}); } if (auto subPattern = pattern->getSubPattern()) { dispatcher_.emit(IsPatternSubPatternsTrap{label, dispatcher_.fetchLabel(subPattern)}); diff --git a/swift/extractor/visitors/SwiftVisitor.h b/swift/extractor/visitors/SwiftVisitor.h index 6380390af82..f9464eafa46 100644 --- a/swift/extractor/visitors/SwiftVisitor.h +++ b/swift/extractor/visitors/SwiftVisitor.h @@ -5,7 +5,6 @@ #include "swift/extractor/visitors/ExprVisitor.h" #include "swift/extractor/visitors/StmtVisitor.h" #include "swift/extractor/visitors/TypeVisitor.h" -#include "swift/extractor/visitors/TypeReprVisitor.h" #include "swift/extractor/visitors/PatternVisitor.h" namespace codeql { @@ -26,13 +25,14 @@ class SwiftVisitor : private SwiftDispatcher { void visit(swift::CaseLabelItem* item) override { stmtVisitor.visitCaseLabelItem(item); } void visit(swift::Expr* expr) override { exprVisitor.visit(expr); } void visit(swift::Pattern* pattern) override { patternVisitor.visit(pattern); } - void visit(swift::TypeRepr* type) override { typeReprVisitor.visit(type); } void visit(swift::TypeBase* type) override { typeVisitor.visit(type); } + void visit(swift::TypeRepr* typeRepr, swift::Type type) override { + typeVisitor.visit(*typeRepr, type); + } DeclVisitor declVisitor{*this}; ExprVisitor exprVisitor{*this}; StmtVisitor stmtVisitor{*this}; - TypeReprVisitor typeReprVisitor{*this}; TypeVisitor typeVisitor{*this}; PatternVisitor patternVisitor{*this}; }; diff --git a/swift/extractor/visitors/TypeReprVisitor.cpp b/swift/extractor/visitors/TypeReprVisitor.cpp deleted file mode 100644 index a3daa7938bb..00000000000 --- a/swift/extractor/visitors/TypeReprVisitor.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include "swift/extractor/visitors/TypeReprVisitor.h" - -namespace codeql {} // namespace codeql diff --git a/swift/extractor/visitors/TypeReprVisitor.h b/swift/extractor/visitors/TypeReprVisitor.h deleted file mode 100644 index dba2198cc6c..00000000000 --- a/swift/extractor/visitors/TypeReprVisitor.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "swift/extractor/visitors/VisitorBase.h" -#include "swift/extractor/trap/generated/typerepr/TrapClasses.h" - -namespace codeql { - -class TypeReprVisitor : public AstVisitorBase { - public: - using AstVisitorBase::AstVisitorBase; -}; - -} // namespace codeql diff --git a/swift/extractor/visitors/TypeVisitor.cpp b/swift/extractor/visitors/TypeVisitor.cpp index 4b6733312d3..5a1896080e0 100644 --- a/swift/extractor/visitors/TypeVisitor.cpp +++ b/swift/extractor/visitors/TypeVisitor.cpp @@ -8,6 +8,12 @@ void TypeVisitor::visit(swift::TypeBase* type) { dispatcher_.emit(TypesTrap{label, type->getString(), canonicalLabel}); } +void TypeVisitor::visit(const swift::TypeRepr& typeRepr, swift::Type type) { + auto entry = dispatcher_.createEntry(typeRepr); + entry.type = dispatcher_.fetchLabel(type); + dispatcher_.emit(entry); +} + void TypeVisitor::visitProtocolType(swift::ProtocolType* type) { auto label = dispatcher_.assignNewLabel(type); dispatcher_.emit(ProtocolTypesTrap{label}); diff --git a/swift/extractor/visitors/TypeVisitor.h b/swift/extractor/visitors/TypeVisitor.h index 77ae8ee13bf..b2deab60369 100644 --- a/swift/extractor/visitors/TypeVisitor.h +++ b/swift/extractor/visitors/TypeVisitor.h @@ -9,6 +9,8 @@ class TypeVisitor : public TypeVisitorBase { using TypeVisitorBase::TypeVisitorBase; void visit(swift::TypeBase* type); + void visit(const swift::TypeRepr& typeRepr, swift::Type type); + void visitProtocolType(swift::ProtocolType* type); void visitEnumType(swift::EnumType* type); void visitStructType(swift::StructType* type); diff --git a/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll b/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll index b9d3cc2a6ec..c3eda3a98f5 100644 --- a/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll +++ b/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll @@ -59,7 +59,7 @@ module CfgScope { private class KeyPathScope extends Range_ instanceof KeyPathExpr { AstControlFlowTree tree; - KeyPathScope() { tree.getAst() = this.getParsedRoot().getFullyConverted() } + KeyPathScope() { tree.getAst() = this } final override predicate entry(ControlFlowElement first) { first(tree, first) } @@ -836,9 +836,6 @@ module Patterns { // Note: `getSubPattern` only has a result if the `is` pattern is of the form `pattern as type`. i = 0 and result.asAstNode() = ast.getSubPattern().getFullyUnresolved() - or - i = 1 and - result.asAstNode() = ast.getCastTypeRepr() } } @@ -1604,8 +1601,14 @@ module Exprs { final override ControlFlowElement getChildElement(int i) { result.asAstNode() = ast.getSubExpr().getFullyConverted() and i = 0 - or - result.asAstNode() = ast.getTypeRepr().getFullyUnresolved() and i = 1 + } + } + + private class IsTree extends AstStandardPostOrderTree { + override IsExpr ast; + + final override ControlFlowElement getChildElement(int i) { + result.asAstNode() = ast.getSubExpr().getFullyConverted() and i = 0 } } diff --git a/swift/ql/lib/codeql/swift/elements.qll b/swift/ql/lib/codeql/swift/elements.qll index c10706d7c7a..5f5b2a04757 100644 --- a/swift/ql/lib/codeql/swift/elements.qll +++ b/swift/ql/lib/codeql/swift/elements.qll @@ -271,6 +271,7 @@ import codeql.swift.elements.type.SyntaxSugarType import codeql.swift.elements.type.TupleType import codeql.swift.elements.type.Type import codeql.swift.elements.type.TypeAliasType +import codeql.swift.elements.type.TypeRepr import codeql.swift.elements.type.TypeVariableType import codeql.swift.elements.type.UnarySyntaxSugarType import codeql.swift.elements.type.UnboundGenericType @@ -279,32 +280,3 @@ import codeql.swift.elements.type.UnownedStorageType import codeql.swift.elements.type.UnresolvedType import codeql.swift.elements.type.VariadicSequenceType import codeql.swift.elements.type.WeakStorageType -import codeql.swift.elements.typerepr.ArrayTypeRepr -import codeql.swift.elements.typerepr.AttributedTypeRepr -import codeql.swift.elements.typerepr.CompileTimeConstTypeRepr -import codeql.swift.elements.typerepr.ComponentIdentTypeRepr -import codeql.swift.elements.typerepr.CompositionTypeRepr -import codeql.swift.elements.typerepr.CompoundIdentTypeRepr -import codeql.swift.elements.typerepr.DictionaryTypeRepr -import codeql.swift.elements.typerepr.ErrorTypeRepr -import codeql.swift.elements.typerepr.ExistentialTypeRepr -import codeql.swift.elements.typerepr.FixedTypeRepr -import codeql.swift.elements.typerepr.FunctionTypeRepr -import codeql.swift.elements.typerepr.GenericIdentTypeRepr -import codeql.swift.elements.typerepr.IdentTypeRepr -import codeql.swift.elements.typerepr.ImplicitlyUnwrappedOptionalTypeRepr -import codeql.swift.elements.typerepr.InOutTypeRepr -import codeql.swift.elements.typerepr.IsolatedTypeRepr -import codeql.swift.elements.typerepr.MetatypeTypeRepr -import codeql.swift.elements.typerepr.NamedOpaqueReturnTypeRepr -import codeql.swift.elements.typerepr.OpaqueReturnTypeRepr -import codeql.swift.elements.typerepr.OptionalTypeRepr -import codeql.swift.elements.typerepr.OwnedTypeRepr -import codeql.swift.elements.typerepr.PlaceholderTypeRepr -import codeql.swift.elements.typerepr.ProtocolTypeRepr -import codeql.swift.elements.typerepr.SharedTypeRepr -import codeql.swift.elements.typerepr.SilBoxTypeRepr -import codeql.swift.elements.typerepr.SimpleIdentTypeRepr -import codeql.swift.elements.typerepr.SpecifierTypeRepr -import codeql.swift.elements.typerepr.TupleTypeRepr -import codeql.swift.elements.typerepr.TypeRepr diff --git a/swift/ql/lib/codeql/swift/elements/type/TypeRepr.qll b/swift/ql/lib/codeql/swift/elements/type/TypeRepr.qll new file mode 100644 index 00000000000..8462ceb1b6b --- /dev/null +++ b/swift/ql/lib/codeql/swift/elements/type/TypeRepr.qll @@ -0,0 +1,5 @@ +private import codeql.swift.generated.type.TypeRepr + +class TypeRepr extends TypeReprBase { + override string toString() { result = getType().toString() } +} diff --git a/swift/ql/lib/codeql/swift/elements/typerepr/ComponentIdentTypeRepr.qll b/swift/ql/lib/codeql/swift/elements/typerepr/ComponentIdentTypeRepr.qll deleted file mode 100644 index 0b5640208f4..00000000000 --- a/swift/ql/lib/codeql/swift/elements/typerepr/ComponentIdentTypeRepr.qll +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py, remove this comment if you wish to edit this file -private import codeql.swift.generated.typerepr.ComponentIdentTypeRepr - -class ComponentIdentTypeRepr extends ComponentIdentTypeReprBase { } diff --git a/swift/ql/lib/codeql/swift/elements/typerepr/CompositionTypeRepr.qll b/swift/ql/lib/codeql/swift/elements/typerepr/CompositionTypeRepr.qll deleted file mode 100644 index 4f79f493e7e..00000000000 --- a/swift/ql/lib/codeql/swift/elements/typerepr/CompositionTypeRepr.qll +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py, remove this comment if you wish to edit this file -private import codeql.swift.generated.typerepr.CompositionTypeRepr - -class CompositionTypeRepr extends CompositionTypeReprBase { } diff --git a/swift/ql/lib/codeql/swift/elements/typerepr/ErrorTypeRepr.qll b/swift/ql/lib/codeql/swift/elements/typerepr/ErrorTypeRepr.qll deleted file mode 100644 index bc3a125e0ac..00000000000 --- a/swift/ql/lib/codeql/swift/elements/typerepr/ErrorTypeRepr.qll +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py, remove this comment if you wish to edit this file -private import codeql.swift.generated.typerepr.ErrorTypeRepr - -class ErrorTypeRepr extends ErrorTypeReprBase { } diff --git a/swift/ql/lib/codeql/swift/elements/typerepr/ExistentialTypeRepr.qll b/swift/ql/lib/codeql/swift/elements/typerepr/ExistentialTypeRepr.qll deleted file mode 100644 index 7762a02bb64..00000000000 --- a/swift/ql/lib/codeql/swift/elements/typerepr/ExistentialTypeRepr.qll +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py, remove this comment if you wish to edit this file -private import codeql.swift.generated.typerepr.ExistentialTypeRepr - -class ExistentialTypeRepr extends ExistentialTypeReprBase { } diff --git a/swift/ql/lib/codeql/swift/elements/typerepr/FixedTypeRepr.qll b/swift/ql/lib/codeql/swift/elements/typerepr/FixedTypeRepr.qll deleted file mode 100644 index 8c1687c8853..00000000000 --- a/swift/ql/lib/codeql/swift/elements/typerepr/FixedTypeRepr.qll +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py, remove this comment if you wish to edit this file -private import codeql.swift.generated.typerepr.FixedTypeRepr - -class FixedTypeRepr extends FixedTypeReprBase { } diff --git a/swift/ql/lib/codeql/swift/elements/typerepr/IdentTypeRepr.qll b/swift/ql/lib/codeql/swift/elements/typerepr/IdentTypeRepr.qll deleted file mode 100644 index 5fa70e354e4..00000000000 --- a/swift/ql/lib/codeql/swift/elements/typerepr/IdentTypeRepr.qll +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py, remove this comment if you wish to edit this file -private import codeql.swift.generated.typerepr.IdentTypeRepr - -class IdentTypeRepr extends IdentTypeReprBase { } diff --git a/swift/ql/lib/codeql/swift/elements/typerepr/NamedOpaqueReturnTypeRepr.qll b/swift/ql/lib/codeql/swift/elements/typerepr/NamedOpaqueReturnTypeRepr.qll deleted file mode 100644 index 115b02b9858..00000000000 --- a/swift/ql/lib/codeql/swift/elements/typerepr/NamedOpaqueReturnTypeRepr.qll +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py, remove this comment if you wish to edit this file -private import codeql.swift.generated.typerepr.NamedOpaqueReturnTypeRepr - -class NamedOpaqueReturnTypeRepr extends NamedOpaqueReturnTypeReprBase { } diff --git a/swift/ql/lib/codeql/swift/elements/typerepr/OpaqueReturnTypeRepr.qll b/swift/ql/lib/codeql/swift/elements/typerepr/OpaqueReturnTypeRepr.qll deleted file mode 100644 index cd0586d67f4..00000000000 --- a/swift/ql/lib/codeql/swift/elements/typerepr/OpaqueReturnTypeRepr.qll +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py, remove this comment if you wish to edit this file -private import codeql.swift.generated.typerepr.OpaqueReturnTypeRepr - -class OpaqueReturnTypeRepr extends OpaqueReturnTypeReprBase { } diff --git a/swift/ql/lib/codeql/swift/elements/typerepr/PlaceholderTypeRepr.qll b/swift/ql/lib/codeql/swift/elements/typerepr/PlaceholderTypeRepr.qll deleted file mode 100644 index 423ced1a5d8..00000000000 --- a/swift/ql/lib/codeql/swift/elements/typerepr/PlaceholderTypeRepr.qll +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py, remove this comment if you wish to edit this file -private import codeql.swift.generated.typerepr.PlaceholderTypeRepr - -class PlaceholderTypeRepr extends PlaceholderTypeReprBase { } diff --git a/swift/ql/lib/codeql/swift/elements/typerepr/SimpleIdentTypeRepr.qll b/swift/ql/lib/codeql/swift/elements/typerepr/SimpleIdentTypeRepr.qll deleted file mode 100644 index 070146f5ace..00000000000 --- a/swift/ql/lib/codeql/swift/elements/typerepr/SimpleIdentTypeRepr.qll +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py, remove this comment if you wish to edit this file -private import codeql.swift.generated.typerepr.SimpleIdentTypeRepr - -class SimpleIdentTypeRepr extends SimpleIdentTypeReprBase { } diff --git a/swift/ql/lib/codeql/swift/elements/typerepr/SpecifierTypeRepr.qll b/swift/ql/lib/codeql/swift/elements/typerepr/SpecifierTypeRepr.qll deleted file mode 100644 index 40d1d648881..00000000000 --- a/swift/ql/lib/codeql/swift/elements/typerepr/SpecifierTypeRepr.qll +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py, remove this comment if you wish to edit this file -private import codeql.swift.generated.typerepr.SpecifierTypeRepr - -class SpecifierTypeRepr extends SpecifierTypeReprBase { } diff --git a/swift/ql/lib/codeql/swift/elements/typerepr/TypeRepr.qll b/swift/ql/lib/codeql/swift/elements/typerepr/TypeRepr.qll deleted file mode 100644 index 4a4ea87833e..00000000000 --- a/swift/ql/lib/codeql/swift/elements/typerepr/TypeRepr.qll +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py, remove this comment if you wish to edit this file -private import codeql.swift.generated.typerepr.TypeRepr - -class TypeRepr extends TypeReprBase { } diff --git a/swift/ql/lib/codeql/swift/generated/GetImmediateParent.qll b/swift/ql/lib/codeql/swift/generated/GetImmediateParent.qll index aaf3dec16bd..0d7bdb0a91d 100644 --- a/swift/ql/lib/codeql/swift/generated/GetImmediateParent.qll +++ b/swift/ql/lib/codeql/swift/generated/GetImmediateParent.qll @@ -64,11 +64,9 @@ Element getAnImmediateChild(Element e) { or dynamic_type_exprs(e, x) or - enum_is_case_exprs(e, x, _, _) + enum_is_case_exprs(e, x, _) or - enum_is_case_exprs(e, _, x, _) - or - enum_is_case_exprs(e, _, _, x) + enum_is_case_exprs(e, _, x) or explicit_cast_exprs(e, x) or @@ -96,7 +94,7 @@ Element getAnImmediateChild(Element e) { or key_path_application_exprs(e, _, x) or - key_path_expr_parsed_roots(e, x) + key_path_expr_roots(e, x) or key_path_expr_parsed_paths(e, x) or diff --git a/swift/ql/lib/codeql/swift/generated/expr/EnumIsCaseExpr.qll b/swift/ql/lib/codeql/swift/generated/expr/EnumIsCaseExpr.qll index e02b97813a4..1dfdb7d0d2c 100644 --- a/swift/ql/lib/codeql/swift/generated/expr/EnumIsCaseExpr.qll +++ b/swift/ql/lib/codeql/swift/generated/expr/EnumIsCaseExpr.qll @@ -1,28 +1,20 @@ // generated by codegen/codegen.py import codeql.swift.elements.decl.EnumElementDecl import codeql.swift.elements.expr.Expr -import codeql.swift.elements.typerepr.TypeRepr class EnumIsCaseExprBase extends @enum_is_case_expr, Expr { override string getAPrimaryQlClass() { result = "EnumIsCaseExpr" } Expr getSubExpr() { exists(Expr x | - enum_is_case_exprs(this, x, _, _) and - result = x.resolve() - ) - } - - TypeRepr getTypeRepr() { - exists(TypeRepr x | - enum_is_case_exprs(this, _, x, _) and + enum_is_case_exprs(this, x, _) and result = x.resolve() ) } EnumElementDecl getElement() { exists(EnumElementDecl x | - enum_is_case_exprs(this, _, _, x) and + enum_is_case_exprs(this, _, x) and result = x.resolve() ) } diff --git a/swift/ql/lib/codeql/swift/generated/expr/KeyPathExpr.qll b/swift/ql/lib/codeql/swift/generated/expr/KeyPathExpr.qll index 7ac3d4614f0..faeeb4fde9b 100644 --- a/swift/ql/lib/codeql/swift/generated/expr/KeyPathExpr.qll +++ b/swift/ql/lib/codeql/swift/generated/expr/KeyPathExpr.qll @@ -1,17 +1,18 @@ // generated by codegen/codegen.py import codeql.swift.elements.expr.Expr +import codeql.swift.elements.type.TypeRepr class KeyPathExprBase extends @key_path_expr, Expr { override string getAPrimaryQlClass() { result = "KeyPathExpr" } - Expr getParsedRoot() { - exists(Expr x | - key_path_expr_parsed_roots(this, x) and + TypeRepr getRoot() { + exists(TypeRepr x | + key_path_expr_roots(this, x) and result = x.resolve() ) } - predicate hasParsedRoot() { exists(getParsedRoot()) } + predicate hasRoot() { exists(getRoot()) } Expr getParsedPath() { exists(Expr x | diff --git a/swift/ql/lib/codeql/swift/generated/expr/TypeExpr.qll b/swift/ql/lib/codeql/swift/generated/expr/TypeExpr.qll index 8b9e238b3ca..3c5816624d1 100644 --- a/swift/ql/lib/codeql/swift/generated/expr/TypeExpr.qll +++ b/swift/ql/lib/codeql/swift/generated/expr/TypeExpr.qll @@ -1,6 +1,6 @@ // generated by codegen/codegen.py import codeql.swift.elements.expr.Expr -import codeql.swift.elements.typerepr.TypeRepr +import codeql.swift.elements.type.TypeRepr class TypeExprBase extends @type_expr, Expr { override string getAPrimaryQlClass() { result = "TypeExpr" } diff --git a/swift/ql/lib/codeql/swift/generated/pattern/IsPattern.qll b/swift/ql/lib/codeql/swift/generated/pattern/IsPattern.qll index 8caf5493b36..b83c0af7d65 100644 --- a/swift/ql/lib/codeql/swift/generated/pattern/IsPattern.qll +++ b/swift/ql/lib/codeql/swift/generated/pattern/IsPattern.qll @@ -1,6 +1,6 @@ // generated by codegen/codegen.py import codeql.swift.elements.pattern.Pattern -import codeql.swift.elements.typerepr.TypeRepr +import codeql.swift.elements.type.TypeRepr class IsPatternBase extends @is_pattern, Pattern { override string getAPrimaryQlClass() { result = "IsPattern" } diff --git a/swift/ql/lib/codeql/swift/generated/pattern/TypedPattern.qll b/swift/ql/lib/codeql/swift/generated/pattern/TypedPattern.qll index 9a16c9cfe02..aebb2a6527c 100644 --- a/swift/ql/lib/codeql/swift/generated/pattern/TypedPattern.qll +++ b/swift/ql/lib/codeql/swift/generated/pattern/TypedPattern.qll @@ -1,6 +1,6 @@ // generated by codegen/codegen.py import codeql.swift.elements.pattern.Pattern -import codeql.swift.elements.typerepr.TypeRepr +import codeql.swift.elements.type.TypeRepr class TypedPatternBase extends @typed_pattern, Pattern { override string getAPrimaryQlClass() { result = "TypedPattern" } diff --git a/swift/ql/lib/codeql/swift/generated/type/TypeRepr.qll b/swift/ql/lib/codeql/swift/generated/type/TypeRepr.qll new file mode 100644 index 00000000000..829960deda9 --- /dev/null +++ b/swift/ql/lib/codeql/swift/generated/type/TypeRepr.qll @@ -0,0 +1,14 @@ +// generated by codegen/codegen.py +import codeql.swift.elements.AstNode +import codeql.swift.elements.type.Type + +class TypeReprBase extends @type_repr, AstNode { + override string getAPrimaryQlClass() { result = "TypeRepr" } + + Type getType() { + exists(Type x | + type_reprs(this, x) and + result = x.resolve() + ) + } +} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/ArrayTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/ArrayTypeRepr.qll deleted file mode 100644 index 486a98a541e..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/ArrayTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.TypeRepr - -class ArrayTypeReprBase extends @array_type_repr, TypeRepr { - override string getAPrimaryQlClass() { result = "ArrayTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/AttributedTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/AttributedTypeRepr.qll deleted file mode 100644 index 96d78c7c933..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/AttributedTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.TypeRepr - -class AttributedTypeReprBase extends @attributed_type_repr, TypeRepr { - override string getAPrimaryQlClass() { result = "AttributedTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/CompileTimeConstTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/CompileTimeConstTypeRepr.qll deleted file mode 100644 index eecb67b9218..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/CompileTimeConstTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.SpecifierTypeRepr - -class CompileTimeConstTypeReprBase extends @compile_time_const_type_repr, SpecifierTypeRepr { - override string getAPrimaryQlClass() { result = "CompileTimeConstTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/ComponentIdentTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/ComponentIdentTypeRepr.qll deleted file mode 100644 index d0323ad681d..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/ComponentIdentTypeRepr.qll +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.IdentTypeRepr - -class ComponentIdentTypeReprBase extends @component_ident_type_repr, IdentTypeRepr { } diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/CompositionTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/CompositionTypeRepr.qll deleted file mode 100644 index b0789100618..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/CompositionTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.TypeRepr - -class CompositionTypeReprBase extends @composition_type_repr, TypeRepr { - override string getAPrimaryQlClass() { result = "CompositionTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/CompoundIdentTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/CompoundIdentTypeRepr.qll deleted file mode 100644 index 7c1c3548272..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/CompoundIdentTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.IdentTypeRepr - -class CompoundIdentTypeReprBase extends @compound_ident_type_repr, IdentTypeRepr { - override string getAPrimaryQlClass() { result = "CompoundIdentTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/DictionaryTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/DictionaryTypeRepr.qll deleted file mode 100644 index 692b9352e20..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/DictionaryTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.TypeRepr - -class DictionaryTypeReprBase extends @dictionary_type_repr, TypeRepr { - override string getAPrimaryQlClass() { result = "DictionaryTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/ErrorTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/ErrorTypeRepr.qll deleted file mode 100644 index 511f7e25569..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/ErrorTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.TypeRepr - -class ErrorTypeReprBase extends @error_type_repr, TypeRepr { - override string getAPrimaryQlClass() { result = "ErrorTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/ExistentialTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/ExistentialTypeRepr.qll deleted file mode 100644 index 43aefeed5a9..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/ExistentialTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.TypeRepr - -class ExistentialTypeReprBase extends @existential_type_repr, TypeRepr { - override string getAPrimaryQlClass() { result = "ExistentialTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/FixedTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/FixedTypeRepr.qll deleted file mode 100644 index 2a6e59a90a8..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/FixedTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.TypeRepr - -class FixedTypeReprBase extends @fixed_type_repr, TypeRepr { - override string getAPrimaryQlClass() { result = "FixedTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/FunctionTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/FunctionTypeRepr.qll deleted file mode 100644 index da781a90d4b..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/FunctionTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.TypeRepr - -class FunctionTypeReprBase extends @function_type_repr, TypeRepr { - override string getAPrimaryQlClass() { result = "FunctionTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/GenericIdentTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/GenericIdentTypeRepr.qll deleted file mode 100644 index 101985aca56..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/GenericIdentTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.ComponentIdentTypeRepr - -class GenericIdentTypeReprBase extends @generic_ident_type_repr, ComponentIdentTypeRepr { - override string getAPrimaryQlClass() { result = "GenericIdentTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/IdentTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/IdentTypeRepr.qll deleted file mode 100644 index d71eca38b92..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/IdentTypeRepr.qll +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.TypeRepr - -class IdentTypeReprBase extends @ident_type_repr, TypeRepr { } diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/ImplicitlyUnwrappedOptionalTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/ImplicitlyUnwrappedOptionalTypeRepr.qll deleted file mode 100644 index e0759e0fa3c..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/ImplicitlyUnwrappedOptionalTypeRepr.qll +++ /dev/null @@ -1,7 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.TypeRepr - -class ImplicitlyUnwrappedOptionalTypeReprBase extends @implicitly_unwrapped_optional_type_repr, - TypeRepr { - override string getAPrimaryQlClass() { result = "ImplicitlyUnwrappedOptionalTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/InOutTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/InOutTypeRepr.qll deleted file mode 100644 index a7adf859579..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/InOutTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.SpecifierTypeRepr - -class InOutTypeReprBase extends @in_out_type_repr, SpecifierTypeRepr { - override string getAPrimaryQlClass() { result = "InOutTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/IsolatedTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/IsolatedTypeRepr.qll deleted file mode 100644 index 63dcdd9e379..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/IsolatedTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.SpecifierTypeRepr - -class IsolatedTypeReprBase extends @isolated_type_repr, SpecifierTypeRepr { - override string getAPrimaryQlClass() { result = "IsolatedTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/MetatypeTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/MetatypeTypeRepr.qll deleted file mode 100644 index e0477db158b..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/MetatypeTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.TypeRepr - -class MetatypeTypeReprBase extends @metatype_type_repr, TypeRepr { - override string getAPrimaryQlClass() { result = "MetatypeTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/NamedOpaqueReturnTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/NamedOpaqueReturnTypeRepr.qll deleted file mode 100644 index af885d21ab4..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/NamedOpaqueReturnTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.TypeRepr - -class NamedOpaqueReturnTypeReprBase extends @named_opaque_return_type_repr, TypeRepr { - override string getAPrimaryQlClass() { result = "NamedOpaqueReturnTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/OpaqueReturnTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/OpaqueReturnTypeRepr.qll deleted file mode 100644 index 7ecb38e22c6..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/OpaqueReturnTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.TypeRepr - -class OpaqueReturnTypeReprBase extends @opaque_return_type_repr, TypeRepr { - override string getAPrimaryQlClass() { result = "OpaqueReturnTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/OptionalTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/OptionalTypeRepr.qll deleted file mode 100644 index 62a24285ef5..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/OptionalTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.TypeRepr - -class OptionalTypeReprBase extends @optional_type_repr, TypeRepr { - override string getAPrimaryQlClass() { result = "OptionalTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/OwnedTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/OwnedTypeRepr.qll deleted file mode 100644 index b24ca1aa597..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/OwnedTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.SpecifierTypeRepr - -class OwnedTypeReprBase extends @owned_type_repr, SpecifierTypeRepr { - override string getAPrimaryQlClass() { result = "OwnedTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/PlaceholderTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/PlaceholderTypeRepr.qll deleted file mode 100644 index 5bb446c9c73..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/PlaceholderTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.TypeRepr - -class PlaceholderTypeReprBase extends @placeholder_type_repr, TypeRepr { - override string getAPrimaryQlClass() { result = "PlaceholderTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/ProtocolTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/ProtocolTypeRepr.qll deleted file mode 100644 index 1b2e7fb2652..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/ProtocolTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.TypeRepr - -class ProtocolTypeReprBase extends @protocol_type_repr, TypeRepr { - override string getAPrimaryQlClass() { result = "ProtocolTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/SharedTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/SharedTypeRepr.qll deleted file mode 100644 index ca9254217c9..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/SharedTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.SpecifierTypeRepr - -class SharedTypeReprBase extends @shared_type_repr, SpecifierTypeRepr { - override string getAPrimaryQlClass() { result = "SharedTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/SilBoxTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/SilBoxTypeRepr.qll deleted file mode 100644 index 47d4585511b..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/SilBoxTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.TypeRepr - -class SilBoxTypeReprBase extends @sil_box_type_repr, TypeRepr { - override string getAPrimaryQlClass() { result = "SilBoxTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/SimpleIdentTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/SimpleIdentTypeRepr.qll deleted file mode 100644 index 328b8bc7e69..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/SimpleIdentTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.ComponentIdentTypeRepr - -class SimpleIdentTypeReprBase extends @simple_ident_type_repr, ComponentIdentTypeRepr { - override string getAPrimaryQlClass() { result = "SimpleIdentTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/SpecifierTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/SpecifierTypeRepr.qll deleted file mode 100644 index 469aa3413d1..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/SpecifierTypeRepr.qll +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.TypeRepr - -class SpecifierTypeReprBase extends @specifier_type_repr, TypeRepr { } diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/TupleTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/TupleTypeRepr.qll deleted file mode 100644 index 79e65037aa9..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/TupleTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.TypeRepr - -class TupleTypeReprBase extends @tuple_type_repr, TypeRepr { - override string getAPrimaryQlClass() { result = "TupleTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/TypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/TypeRepr.qll deleted file mode 100644 index 497ac46fa64..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/TypeRepr.qll +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.AstNode - -class TypeReprBase extends @type_repr, AstNode { } diff --git a/swift/ql/lib/swift.dbscheme b/swift/ql/lib/swift.dbscheme index 83ee8412131..61cda563d66 100644 --- a/swift/ql/lib/swift.dbscheme +++ b/swift/ql/lib/swift.dbscheme @@ -471,27 +471,10 @@ expr_types( //dir=expr | @yield_stmt ; -@type_repr = - @array_type_repr -| @attributed_type_repr -| @composition_type_repr -| @dictionary_type_repr -| @error_type_repr -| @existential_type_repr -| @fixed_type_repr -| @function_type_repr -| @ident_type_repr -| @implicitly_unwrapped_optional_type_repr -| @metatype_type_repr -| @named_opaque_return_type_repr -| @opaque_return_type_repr -| @optional_type_repr -| @placeholder_type_repr -| @protocol_type_repr -| @sil_box_type_repr -| @specifier_type_repr -| @tuple_type_repr -; +type_reprs( //dir=type + unique int id: @type_repr, + int type_: @type ref +); function_types( //dir=type unique int id: @function_type @@ -865,7 +848,6 @@ editor_placeholder_exprs( //dir=expr enum_is_case_exprs( //dir=expr unique int id: @enum_is_case_expr, int sub_expr: @expr ref, - int type_repr: @type_repr ref, int element: @enum_element_decl ref ); @@ -969,9 +951,9 @@ key_path_exprs( //dir=expr ); #keyset[id] -key_path_expr_parsed_roots( //dir=expr +key_path_expr_roots( //dir=expr int id: @key_path_expr ref, - int parsed_root: @expr ref + int root: @type_repr ref ); #keyset[id] @@ -2177,121 +2159,3 @@ integer_literal_exprs( //dir=expr unique int id: @integer_literal_expr, string string_value: string ref ); - -error_type_reprs( //dir=typerepr - unique int id: @error_type_repr -); - -attributed_type_reprs( //dir=typerepr - unique int id: @attributed_type_repr -); - -@ident_type_repr = - @component_ident_type_repr -| @compound_ident_type_repr -; - -@component_ident_type_repr = - @generic_ident_type_repr -| @simple_ident_type_repr -; - -simple_ident_type_reprs( //dir=typerepr - unique int id: @simple_ident_type_repr -); - -generic_ident_type_reprs( //dir=typerepr - unique int id: @generic_ident_type_repr -); - -compound_ident_type_reprs( //dir=typerepr - unique int id: @compound_ident_type_repr -); - -function_type_reprs( //dir=typerepr - unique int id: @function_type_repr -); - -array_type_reprs( //dir=typerepr - unique int id: @array_type_repr -); - -dictionary_type_reprs( //dir=typerepr - unique int id: @dictionary_type_repr -); - -optional_type_reprs( //dir=typerepr - unique int id: @optional_type_repr -); - -implicitly_unwrapped_optional_type_reprs( //dir=typerepr - unique int id: @implicitly_unwrapped_optional_type_repr -); - -tuple_type_reprs( //dir=typerepr - unique int id: @tuple_type_repr -); - -composition_type_reprs( //dir=typerepr - unique int id: @composition_type_repr -); - -metatype_type_reprs( //dir=typerepr - unique int id: @metatype_type_repr -); - -protocol_type_reprs( //dir=typerepr - unique int id: @protocol_type_repr -); - -opaque_return_type_reprs( //dir=typerepr - unique int id: @opaque_return_type_repr -); - -named_opaque_return_type_reprs( //dir=typerepr - unique int id: @named_opaque_return_type_repr -); - -existential_type_reprs( //dir=typerepr - unique int id: @existential_type_repr -); - -placeholder_type_reprs( //dir=typerepr - unique int id: @placeholder_type_repr -); - -@specifier_type_repr = - @compile_time_const_type_repr -| @in_out_type_repr -| @isolated_type_repr -| @owned_type_repr -| @shared_type_repr -; - -in_out_type_reprs( //dir=typerepr - unique int id: @in_out_type_repr -); - -shared_type_reprs( //dir=typerepr - unique int id: @shared_type_repr -); - -owned_type_reprs( //dir=typerepr - unique int id: @owned_type_repr -); - -isolated_type_reprs( //dir=typerepr - unique int id: @isolated_type_repr -); - -compile_time_const_type_reprs( //dir=typerepr - unique int id: @compile_time_const_type_repr -); - -fixed_type_reprs( //dir=typerepr - unique int id: @fixed_type_repr -); - -sil_box_type_reprs( //dir=typerepr - unique int id: @sil_box_type_repr -); diff --git a/swift/ql/test/extractor-tests/expressions/all.expected b/swift/ql/test/extractor-tests/expressions/all.expected index 20951358f73..b34ca7349cd 100644 --- a/swift/ql/test/extractor-tests/expressions/all.expected +++ b/swift/ql/test/extractor-tests/expressions/all.expected @@ -123,8 +123,6 @@ | expressions.swift:54:1:54:1 | _ | DiscardAssignmentExpr | | expressions.swift:54:1:54:8 | ... = ... | AssignExpr | | expressions.swift:54:5:54:8 | #keyPath(...) | KeyPathExpr | -| expressions.swift:54:6:54:6 | (no string representation) | TypeExpr | -| expressions.swift:54:6:54:8 | ... .x | UnresolvedDotExpr | | expressions.swift:58:16:58:16 | 1234 | IntegerLiteralExpr | | expressions.swift:59:1:59:1 | unsafeFunction(pointer:) | DeclRefExpr | | expressions.swift:59:1:59:34 | call to unsafeFunction(pointer:) | CallExpr | @@ -242,5 +240,3 @@ | expressions.swift:154:22:154:56 | \\...[...] | KeyPathApplicationExpr | | expressions.swift:154:33:154:33 | keyPathB | DeclRefExpr | | expressions.swift:154:52:154:55 | #keyPath(...) | KeyPathExpr | -| expressions.swift:154:53:154:53 | (no string representation) | TypeExpr | -| expressions.swift:154:53:154:55 | ... .x | UnresolvedDotExpr | diff --git a/swift/ql/test/extractor-tests/generated/expr/EnumIsCaseExpr/EnumIsCaseExpr.expected b/swift/ql/test/extractor-tests/generated/expr/EnumIsCaseExpr/EnumIsCaseExpr.expected index 9b22d5b34d4..6076a256058 100644 --- a/swift/ql/test/extractor-tests/generated/expr/EnumIsCaseExpr/EnumIsCaseExpr.expected +++ b/swift/ql/test/extractor-tests/generated/expr/EnumIsCaseExpr/EnumIsCaseExpr.expected @@ -1,10 +1,10 @@ -| enum_is_case.swift:4:1:4:17 | ... is some | getSubExpr: | enum_is_case.swift:4:1:4:17 | OptionalEvaluationExpr | getTypeRepr: | enum_is_case.swift:4:22:4:22 | SimpleIdentTypeRepr | getElement: | file://:0:0:0:0 | some | -| enum_is_case.swift:5:1:5:32 | ... is some | getSubExpr: | enum_is_case.swift:5:1:5:32 | OptionalEvaluationExpr | getTypeRepr: | enum_is_case.swift:5:37:5:37 | SimpleIdentTypeRepr | getElement: | file://:0:0:0:0 | some | -| enum_is_case.swift:6:1:6:1 | ... is some | getSubExpr: | enum_is_case.swift:6:1:6:1 | 42 | getTypeRepr: | enum_is_case.swift:6:7:6:10 | ...? | getElement: | file://:0:0:0:0 | some | -| enum_is_case.swift:7:1:7:1 | ... is some | getSubExpr: | enum_is_case.swift:7:1:7:1 | 42 | getTypeRepr: | enum_is_case.swift:7:7:7:11 | ...? | getElement: | file://:0:0:0:0 | some | -| enum_is_case.swift:9:1:9:19 | ... is some | getSubExpr: | enum_is_case.swift:9:1:9:19 | [...] | getTypeRepr: | enum_is_case.swift:9:24:9:28 | [...] | getElement: | file://:0:0:0:0 | some | -| enum_is_case.swift:19:1:19:18 | ... is some | getSubExpr: | enum_is_case.swift:19:1:19:18 | OptionalEvaluationExpr | getTypeRepr: | enum_is_case.swift:19:23:19:23 | SimpleIdentTypeRepr | getElement: | file://:0:0:0:0 | some | -| enum_is_case.swift:21:1:21:5 | ... is some | getSubExpr: | enum_is_case.swift:21:1:21:5 | [...] | getTypeRepr: | enum_is_case.swift:21:10:21:12 | [...] | getElement: | file://:0:0:0:0 | some | -| enum_is_case.swift:22:1:22:10 | ... is some | getSubExpr: | enum_is_case.swift:22:1:22:10 | [...] | getTypeRepr: | enum_is_case.swift:22:15:22:25 | [... : ...] | getElement: | file://:0:0:0:0 | some | -| enum_is_case.swift:23:1:23:10 | ... is some | getSubExpr: | enum_is_case.swift:23:1:23:10 | [...] | getTypeRepr: | enum_is_case.swift:23:15:23:25 | [... : ...] | getElement: | file://:0:0:0:0 | some | -| enum_is_case.swift:24:1:24:8 | ... is some | getSubExpr: | enum_is_case.swift:24:1:24:8 | call to ... | getTypeRepr: | enum_is_case.swift:24:13:24:18 | ...<...> | getElement: | file://:0:0:0:0 | some | +| enum_is_case.swift:4:1:4:17 | ... is some | getSubExpr: | enum_is_case.swift:4:1:4:17 | OptionalEvaluationExpr | getElement: | file://:0:0:0:0 | some | +| enum_is_case.swift:5:1:5:32 | ... is some | getSubExpr: | enum_is_case.swift:5:1:5:32 | OptionalEvaluationExpr | getElement: | file://:0:0:0:0 | some | +| enum_is_case.swift:6:1:6:1 | ... is some | getSubExpr: | enum_is_case.swift:6:1:6:1 | 42 | getElement: | file://:0:0:0:0 | some | +| enum_is_case.swift:7:1:7:1 | ... is some | getSubExpr: | enum_is_case.swift:7:1:7:1 | 42 | getElement: | file://:0:0:0:0 | some | +| enum_is_case.swift:9:1:9:19 | ... is some | getSubExpr: | enum_is_case.swift:9:1:9:19 | [...] | getElement: | file://:0:0:0:0 | some | +| enum_is_case.swift:19:1:19:18 | ... is some | getSubExpr: | enum_is_case.swift:19:1:19:18 | OptionalEvaluationExpr | getElement: | file://:0:0:0:0 | some | +| enum_is_case.swift:21:1:21:5 | ... is some | getSubExpr: | enum_is_case.swift:21:1:21:5 | [...] | getElement: | file://:0:0:0:0 | some | +| enum_is_case.swift:22:1:22:10 | ... is some | getSubExpr: | enum_is_case.swift:22:1:22:10 | [...] | getElement: | file://:0:0:0:0 | some | +| enum_is_case.swift:23:1:23:10 | ... is some | getSubExpr: | enum_is_case.swift:23:1:23:10 | [...] | getElement: | file://:0:0:0:0 | some | +| enum_is_case.swift:24:1:24:8 | ... is some | getSubExpr: | enum_is_case.swift:24:1:24:8 | call to ... | getElement: | file://:0:0:0:0 | some | diff --git a/swift/ql/test/extractor-tests/generated/expr/EnumIsCaseExpr/EnumIsCaseExpr.ql b/swift/ql/test/extractor-tests/generated/expr/EnumIsCaseExpr/EnumIsCaseExpr.ql index e35700dcbe0..9c3340d4ff5 100644 --- a/swift/ql/test/extractor-tests/generated/expr/EnumIsCaseExpr/EnumIsCaseExpr.ql +++ b/swift/ql/test/extractor-tests/generated/expr/EnumIsCaseExpr/EnumIsCaseExpr.ql @@ -2,11 +2,10 @@ import codeql.swift.elements import TestUtils -from EnumIsCaseExpr x, Expr getSubExpr, TypeRepr getTypeRepr, EnumElementDecl getElement +from EnumIsCaseExpr x, Expr getSubExpr, EnumElementDecl getElement where toBeTested(x) and not x.isUnknown() and getSubExpr = x.getSubExpr() and - getTypeRepr = x.getTypeRepr() and getElement = x.getElement() -select x, "getSubExpr:", getSubExpr, "getTypeRepr:", getTypeRepr, "getElement:", getElement +select x, "getSubExpr:", getSubExpr, "getElement:", getElement diff --git a/swift/ql/test/extractor-tests/generated/typerepr/ArrayTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/expr/UnresolvedDotExpr/MISSING_SOURCE.txt similarity index 100% rename from swift/ql/test/extractor-tests/generated/typerepr/ArrayTypeRepr/MISSING_SOURCE.txt rename to swift/ql/test/extractor-tests/generated/expr/UnresolvedDotExpr/MISSING_SOURCE.txt diff --git a/swift/ql/test/extractor-tests/generated/expr/UnresolvedDotExpr/UnresolvedDotExpr.expected b/swift/ql/test/extractor-tests/generated/expr/UnresolvedDotExpr/UnresolvedDotExpr.expected deleted file mode 100644 index 2bb615eb361..00000000000 --- a/swift/ql/test/extractor-tests/generated/expr/UnresolvedDotExpr/UnresolvedDotExpr.expected +++ /dev/null @@ -1,3 +0,0 @@ -| unresolved_dot_expr.swift:5:6:5:8 | ... .x | getBase: | unresolved_dot_expr.swift:5:6:5:6 | (no string representation) | getName: | x | -| unresolved_dot_expr.swift:11:6:11:8 | ... .a | getBase: | unresolved_dot_expr.swift:11:6:11:6 | (no string representation) | getName: | a | -| unresolved_dot_expr.swift:11:6:11:10 | ... .x | getBase: | unresolved_dot_expr.swift:11:6:11:8 | ... .a | getName: | x | diff --git a/swift/ql/test/extractor-tests/generated/expr/UnresolvedDotExpr/UnresolvedDotExpr.ql b/swift/ql/test/extractor-tests/generated/expr/UnresolvedDotExpr/UnresolvedDotExpr.ql deleted file mode 100644 index 29327a3f86c..00000000000 --- a/swift/ql/test/extractor-tests/generated/expr/UnresolvedDotExpr/UnresolvedDotExpr.ql +++ /dev/null @@ -1,11 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements -import TestUtils - -from UnresolvedDotExpr x, Expr getBase, string getName -where - toBeTested(x) and - not x.isUnknown() and - getBase = x.getBase() and - getName = x.getName() -select x, "getBase:", getBase, "getName:", getName diff --git a/swift/ql/test/extractor-tests/generated/expr/UnresolvedDotExpr/UnresolvedDotExpr_getType.expected b/swift/ql/test/extractor-tests/generated/expr/UnresolvedDotExpr/UnresolvedDotExpr_getType.expected deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/swift/ql/test/extractor-tests/generated/expr/UnresolvedDotExpr/UnresolvedDotExpr_getType.ql b/swift/ql/test/extractor-tests/generated/expr/UnresolvedDotExpr/UnresolvedDotExpr_getType.ql deleted file mode 100644 index f91cc957ef7..00000000000 --- a/swift/ql/test/extractor-tests/generated/expr/UnresolvedDotExpr/UnresolvedDotExpr_getType.ql +++ /dev/null @@ -1,7 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements -import TestUtils - -from UnresolvedDotExpr x -where toBeTested(x) and not x.isUnknown() -select x, x.getType() diff --git a/swift/ql/test/extractor-tests/generated/expr/UnresolvedDotExpr/unresolved_dot_expr.swift b/swift/ql/test/extractor-tests/generated/expr/UnresolvedDotExpr/unresolved_dot_expr.swift deleted file mode 100644 index 3003e6efe82..00000000000 --- a/swift/ql/test/extractor-tests/generated/expr/UnresolvedDotExpr/unresolved_dot_expr.swift +++ /dev/null @@ -1,11 +0,0 @@ -struct A { - var x: Int = 42 -} - -_ = \A.x - -struct B { - var a: A -} - -_ = \B.a.x diff --git a/swift/ql/test/extractor-tests/generated/typerepr/AttributedTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/type/TypeRepr/MISSING_SOURCE.txt similarity index 100% rename from swift/ql/test/extractor-tests/generated/typerepr/AttributedTypeRepr/MISSING_SOURCE.txt rename to swift/ql/test/extractor-tests/generated/type/TypeRepr/MISSING_SOURCE.txt diff --git a/swift/ql/test/extractor-tests/generated/typerepr/CompileTimeConstTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/CompileTimeConstTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/CompileTimeConstTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/CompositionTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/CompositionTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/CompositionTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/CompoundIdentTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/CompoundIdentTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/CompoundIdentTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/DictionaryTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/DictionaryTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/DictionaryTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/ErrorTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/ErrorTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/ErrorTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/ExistentialTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/ExistentialTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/ExistentialTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/FixedTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/FixedTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/FixedTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/FunctionTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/FunctionTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/FunctionTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/GenericIdentTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/GenericIdentTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/GenericIdentTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/ImplicitlyUnwrappedOptionalTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/ImplicitlyUnwrappedOptionalTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/ImplicitlyUnwrappedOptionalTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/InOutTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/InOutTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/InOutTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/IsolatedTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/IsolatedTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/IsolatedTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/MetatypeTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/MetatypeTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/MetatypeTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/NamedOpaqueReturnTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/NamedOpaqueReturnTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/NamedOpaqueReturnTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/OpaqueReturnTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/OpaqueReturnTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/OpaqueReturnTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/OptionalTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/OptionalTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/OptionalTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/OwnedTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/OwnedTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/OwnedTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/PlaceholderTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/PlaceholderTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/PlaceholderTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/ProtocolTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/ProtocolTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/ProtocolTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/SharedTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/SharedTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/SharedTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/SilBoxTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/SilBoxTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/SilBoxTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/SimpleIdentTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/SimpleIdentTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/SimpleIdentTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/TupleTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/TupleTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/TupleTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/library-tests/controlflow/graph/Cfg.expected b/swift/ql/test/library-tests/controlflow/graph/Cfg.expected index 4d38d88d82c..d14ec33b48e 100644 --- a/swift/ql/test/library-tests/controlflow/graph/Cfg.expected +++ b/swift/ql/test/library-tests/controlflow/graph/Cfg.expected @@ -281,15 +281,12 @@ cfg.swift: #-----| -> ... is ... # 37| ... is ... -#-----| -> SimpleIdentTypeRepr +#-----| -> ... is ... # 37| ... is ... #-----| match -> print(_:separator:terminator:) #-----| no-match -> case ... -# 37| SimpleIdentTypeRepr -#-----| -> ... is ... - # 38| print(_:separator:terminator:) #-----| -> MyError @@ -5575,15 +5572,39 @@ cfg.swift: # 456| var ... = ... #-----| -> kpGet_b_x +# 456| var ... = ... +#-----| -> kpGet_b_x + # 456| kpGet_b_x #-----| match -> #keyPath(...) # 456| kpGet_b_x #-----| -> kpGet_bs_0_x +# 456| kpGet_b_x +#-----| -> kpGet_bs_0_x + # 456| #keyPath(...) #-----| -> var ... = ... +# 456| #keyPath(...) +#-----| -> var ... = ... +#-----| -> exit #keyPath(...) (normal) + +# 456| enter #keyPath(...) +#-----| -> #keyPath(...) + +# 456| exit #keyPath(...) + +# 456| exit #keyPath(...) (normal) +#-----| -> exit #keyPath(...) + +# 457| var ... = ... +#-----| -> kpGet_bs_0_x + +# 457| var ... = ... +#-----| -> kpGet_bs_0_x + # 457| var ... = ... #-----| -> kpGet_bs_0_x @@ -5593,9 +5614,42 @@ cfg.swift: # 457| kpGet_bs_0_x #-----| -> kpGet_mayB_force_x +# 457| kpGet_bs_0_x +#-----| match -> #keyPath(...) + +# 457| kpGet_bs_0_x +#-----| -> kpGet_mayB_force_x + +# 457| kpGet_bs_0_x +#-----| -> kpGet_mayB_force_x + # 457| #keyPath(...) #-----| -> var ... = ... +# 457| #keyPath(...) +#-----| -> var ... = ... + +# 457| #keyPath(...) +#-----| -> var ... = ... +#-----| -> exit #keyPath(...) (normal) + +# 457| enter #keyPath(...) +#-----| -> #keyPath(...) + +# 457| exit #keyPath(...) + +# 457| exit #keyPath(...) (normal) +#-----| -> exit #keyPath(...) + +# 458| var ... = ... +#-----| -> kpGet_mayB_force_x + +# 458| var ... = ... +#-----| -> kpGet_mayB_force_x + +# 458| var ... = ... +#-----| -> kpGet_mayB_force_x + # 458| var ... = ... #-----| -> kpGet_mayB_force_x @@ -5605,9 +5659,54 @@ cfg.swift: # 458| kpGet_mayB_force_x #-----| -> kpGet_mayB_x +# 458| kpGet_mayB_force_x +#-----| match -> #keyPath(...) + +# 458| kpGet_mayB_force_x +#-----| -> kpGet_mayB_x + +# 458| kpGet_mayB_force_x +#-----| match -> #keyPath(...) + +# 458| kpGet_mayB_force_x +#-----| -> kpGet_mayB_x + +# 458| kpGet_mayB_force_x +#-----| -> kpGet_mayB_x + # 458| #keyPath(...) #-----| -> var ... = ... +# 458| #keyPath(...) +#-----| -> var ... = ... + +# 458| #keyPath(...) +#-----| -> var ... = ... + +# 458| #keyPath(...) +#-----| -> var ... = ... +#-----| -> exit #keyPath(...) (normal) + +# 458| enter #keyPath(...) +#-----| -> #keyPath(...) + +# 458| exit #keyPath(...) + +# 458| exit #keyPath(...) (normal) +#-----| -> exit #keyPath(...) + +# 459| var ... = ... +#-----| -> kpGet_mayB_x + +# 459| var ... = ... +#-----| -> kpGet_mayB_x + +# 459| var ... = ... +#-----| -> kpGet_mayB_x + +# 459| var ... = ... +#-----| -> kpGet_mayB_x + # 459| var ... = ... #-----| -> kpGet_mayB_x @@ -5617,9 +5716,63 @@ cfg.swift: # 459| kpGet_mayB_x #-----| -> apply_kpGet_b_x +# 459| kpGet_mayB_x +#-----| match -> #keyPath(...) + +# 459| kpGet_mayB_x +#-----| -> apply_kpGet_b_x + +# 459| kpGet_mayB_x +#-----| match -> #keyPath(...) + +# 459| kpGet_mayB_x +#-----| -> apply_kpGet_b_x + +# 459| kpGet_mayB_x +#-----| match -> #keyPath(...) + +# 459| kpGet_mayB_x +#-----| -> apply_kpGet_b_x + +# 459| kpGet_mayB_x +#-----| -> apply_kpGet_b_x + # 459| #keyPath(...) #-----| -> var ... = ... +# 459| #keyPath(...) +#-----| -> var ... = ... + +# 459| #keyPath(...) +#-----| -> var ... = ... + +# 459| #keyPath(...) +#-----| -> var ... = ... + +# 459| #keyPath(...) +#-----| -> var ... = ... +#-----| -> exit #keyPath(...) (normal) + +# 459| enter #keyPath(...) +#-----| -> #keyPath(...) + +# 459| exit #keyPath(...) + +# 459| exit #keyPath(...) (normal) +#-----| -> exit #keyPath(...) + +# 461| var ... = ... +#-----| -> apply_kpGet_b_x + +# 461| var ... = ... +#-----| -> apply_kpGet_b_x + +# 461| var ... = ... +#-----| -> apply_kpGet_b_x + +# 461| var ... = ... +#-----| -> apply_kpGet_b_x + # 461| var ... = ... #-----| -> apply_kpGet_b_x @@ -5629,18 +5782,102 @@ cfg.swift: # 461| apply_kpGet_b_x #-----| -> apply_kpGet_bs_0_x +# 461| apply_kpGet_b_x +#-----| match -> a + +# 461| apply_kpGet_b_x +#-----| -> apply_kpGet_bs_0_x + +# 461| apply_kpGet_b_x +#-----| match -> a + +# 461| apply_kpGet_b_x +#-----| -> apply_kpGet_bs_0_x + +# 461| apply_kpGet_b_x +#-----| match -> a + +# 461| apply_kpGet_b_x +#-----| -> apply_kpGet_bs_0_x + +# 461| apply_kpGet_b_x +#-----| match -> a + +# 461| apply_kpGet_b_x +#-----| -> apply_kpGet_bs_0_x + # 461| a #-----| -> kpGet_b_x +# 461| a +#-----| -> kpGet_b_x + +# 461| a +#-----| -> kpGet_b_x + +# 461| a +#-----| -> kpGet_b_x + +# 461| a +#-----| -> kpGet_b_x + +# 461| \...[...] +#-----| -> var ... = ... + +# 461| \...[...] +#-----| -> var ... = ... + +# 461| \...[...] +#-----| -> var ... = ... + +# 461| \...[...] +#-----| -> var ... = ... + # 461| \...[...] #-----| -> var ... = ... # 461| (WritableKeyPath) ... #-----| -> \...[...] +# 461| (WritableKeyPath) ... +#-----| -> \...[...] + +# 461| (WritableKeyPath) ... +#-----| -> \...[...] + +# 461| (WritableKeyPath) ... +#-----| -> \...[...] + +# 461| (WritableKeyPath) ... +#-----| -> \...[...] + # 461| kpGet_b_x #-----| -> (WritableKeyPath) ... +# 461| kpGet_b_x +#-----| -> (WritableKeyPath) ... + +# 461| kpGet_b_x +#-----| -> (WritableKeyPath) ... + +# 461| kpGet_b_x +#-----| -> (WritableKeyPath) ... + +# 461| kpGet_b_x +#-----| -> (WritableKeyPath) ... + +# 462| var ... = ... +#-----| -> apply_kpGet_bs_0_x + +# 462| var ... = ... +#-----| -> apply_kpGet_bs_0_x + +# 462| var ... = ... +#-----| -> apply_kpGet_bs_0_x + +# 462| var ... = ... +#-----| -> apply_kpGet_bs_0_x + # 462| var ... = ... #-----| -> apply_kpGet_bs_0_x @@ -5650,18 +5887,102 @@ cfg.swift: # 462| apply_kpGet_bs_0_x #-----| -> apply_kpGet_mayB_force_x +# 462| apply_kpGet_bs_0_x +#-----| match -> a + +# 462| apply_kpGet_bs_0_x +#-----| -> apply_kpGet_mayB_force_x + +# 462| apply_kpGet_bs_0_x +#-----| match -> a + +# 462| apply_kpGet_bs_0_x +#-----| -> apply_kpGet_mayB_force_x + +# 462| apply_kpGet_bs_0_x +#-----| match -> a + +# 462| apply_kpGet_bs_0_x +#-----| -> apply_kpGet_mayB_force_x + +# 462| apply_kpGet_bs_0_x +#-----| match -> a + +# 462| apply_kpGet_bs_0_x +#-----| -> apply_kpGet_mayB_force_x + # 462| a #-----| -> kpGet_bs_0_x +# 462| a +#-----| -> kpGet_bs_0_x + +# 462| a +#-----| -> kpGet_bs_0_x + +# 462| a +#-----| -> kpGet_bs_0_x + +# 462| a +#-----| -> kpGet_bs_0_x + +# 462| \...[...] +#-----| -> var ... = ... + +# 462| \...[...] +#-----| -> var ... = ... + +# 462| \...[...] +#-----| -> var ... = ... + +# 462| \...[...] +#-----| -> var ... = ... + # 462| \...[...] #-----| -> var ... = ... # 462| (WritableKeyPath) ... #-----| -> \...[...] +# 462| (WritableKeyPath) ... +#-----| -> \...[...] + +# 462| (WritableKeyPath) ... +#-----| -> \...[...] + +# 462| (WritableKeyPath) ... +#-----| -> \...[...] + +# 462| (WritableKeyPath) ... +#-----| -> \...[...] + # 462| kpGet_bs_0_x #-----| -> (WritableKeyPath) ... +# 462| kpGet_bs_0_x +#-----| -> (WritableKeyPath) ... + +# 462| kpGet_bs_0_x +#-----| -> (WritableKeyPath) ... + +# 462| kpGet_bs_0_x +#-----| -> (WritableKeyPath) ... + +# 462| kpGet_bs_0_x +#-----| -> (WritableKeyPath) ... + +# 463| var ... = ... +#-----| -> apply_kpGet_mayB_force_x + +# 463| var ... = ... +#-----| -> apply_kpGet_mayB_force_x + +# 463| var ... = ... +#-----| -> apply_kpGet_mayB_force_x + +# 463| var ... = ... +#-----| -> apply_kpGet_mayB_force_x + # 463| var ... = ... #-----| -> apply_kpGet_mayB_force_x @@ -5671,18 +5992,102 @@ cfg.swift: # 463| apply_kpGet_mayB_force_x #-----| -> apply_kpGet_mayB_x +# 463| apply_kpGet_mayB_force_x +#-----| match -> a + +# 463| apply_kpGet_mayB_force_x +#-----| -> apply_kpGet_mayB_x + +# 463| apply_kpGet_mayB_force_x +#-----| match -> a + +# 463| apply_kpGet_mayB_force_x +#-----| -> apply_kpGet_mayB_x + +# 463| apply_kpGet_mayB_force_x +#-----| match -> a + +# 463| apply_kpGet_mayB_force_x +#-----| -> apply_kpGet_mayB_x + +# 463| apply_kpGet_mayB_force_x +#-----| match -> a + +# 463| apply_kpGet_mayB_force_x +#-----| -> apply_kpGet_mayB_x + # 463| a #-----| -> kpGet_mayB_force_x +# 463| a +#-----| -> kpGet_mayB_force_x + +# 463| a +#-----| -> kpGet_mayB_force_x + +# 463| a +#-----| -> kpGet_mayB_force_x + +# 463| a +#-----| -> kpGet_mayB_force_x + +# 463| \...[...] +#-----| -> var ... = ... + +# 463| \...[...] +#-----| -> var ... = ... + +# 463| \...[...] +#-----| -> var ... = ... + +# 463| \...[...] +#-----| -> var ... = ... + # 463| \...[...] #-----| -> var ... = ... # 463| (WritableKeyPath) ... #-----| -> \...[...] +# 463| (WritableKeyPath) ... +#-----| -> \...[...] + +# 463| (WritableKeyPath) ... +#-----| -> \...[...] + +# 463| (WritableKeyPath) ... +#-----| -> \...[...] + +# 463| (WritableKeyPath) ... +#-----| -> \...[...] + # 463| kpGet_mayB_force_x #-----| -> (WritableKeyPath) ... +# 463| kpGet_mayB_force_x +#-----| -> (WritableKeyPath) ... + +# 463| kpGet_mayB_force_x +#-----| -> (WritableKeyPath) ... + +# 463| kpGet_mayB_force_x +#-----| -> (WritableKeyPath) ... + +# 463| kpGet_mayB_force_x +#-----| -> (WritableKeyPath) ... + +# 464| var ... = ... +#-----| -> apply_kpGet_mayB_x + +# 464| var ... = ... +#-----| -> apply_kpGet_mayB_x + +# 464| var ... = ... +#-----| -> apply_kpGet_mayB_x + +# 464| var ... = ... +#-----| -> apply_kpGet_mayB_x + # 464| var ... = ... #-----| -> apply_kpGet_mayB_x @@ -5692,14 +6097,82 @@ cfg.swift: # 464| apply_kpGet_mayB_x #-----| -> exit test(a:) (normal) +# 464| apply_kpGet_mayB_x +#-----| match -> a + +# 464| apply_kpGet_mayB_x + +# 464| apply_kpGet_mayB_x +#-----| match -> a + +# 464| apply_kpGet_mayB_x + +# 464| apply_kpGet_mayB_x +#-----| match -> a + +# 464| apply_kpGet_mayB_x + +# 464| apply_kpGet_mayB_x +#-----| match -> a + +# 464| apply_kpGet_mayB_x + # 464| a #-----| -> kpGet_mayB_x +# 464| a +#-----| -> kpGet_mayB_x + +# 464| a +#-----| -> kpGet_mayB_x + +# 464| a +#-----| -> kpGet_mayB_x + +# 464| a +#-----| -> kpGet_mayB_x + +# 464| \...[...] +#-----| -> var ... = ... + +# 464| \...[...] +#-----| -> var ... = ... + +# 464| \...[...] +#-----| -> var ... = ... + +# 464| \...[...] +#-----| -> var ... = ... + # 464| \...[...] #-----| -> var ... = ... # 464| (KeyPath) ... #-----| -> \...[...] +# 464| (KeyPath) ... +#-----| -> \...[...] + +# 464| (KeyPath) ... +#-----| -> \...[...] + +# 464| (KeyPath) ... +#-----| -> \...[...] + +# 464| (KeyPath) ... +#-----| -> \...[...] + +# 464| kpGet_mayB_x +#-----| -> (KeyPath) ... + +# 464| kpGet_mayB_x +#-----| -> (KeyPath) ... + +# 464| kpGet_mayB_x +#-----| -> (KeyPath) ... + +# 464| kpGet_mayB_x +#-----| -> (KeyPath) ... + # 464| kpGet_mayB_x #-----| -> (KeyPath) ... diff --git a/swift/ql/test/library-tests/parent/parent.expected b/swift/ql/test/library-tests/parent/parent.expected index ab98c789f82..8ebbf1dd372 100644 --- a/swift/ql/test/library-tests/parent/parent.expected +++ b/swift/ql/test/library-tests/parent/parent.expected @@ -22,13 +22,13 @@ | declarations.swift:3:7:3:7 | yield ... | YieldStmt | file://:0:0:0:0 | &... | InOutExpr | | declarations.swift:3:7:3:7 | { ... } | BraceStmt | declarations.swift:3:7:3:7 | yield ... | YieldStmt | | declarations.swift:3:7:3:14 | ... as ... | TypedPattern | declarations.swift:3:7:3:7 | next | NamedPattern | -| declarations.swift:3:7:3:14 | ... as ... | TypedPattern | declarations.swift:3:14:3:14 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| declarations.swift:3:7:3:14 | ... as ... | TypedPattern | declarations.swift:3:14:3:14 | Int | TypeRepr | | declarations.swift:4:5:4:24 | get | AccessorDecl | declarations.swift:4:9:4:24 | { ... } | BraceStmt | | declarations.swift:4:9:4:24 | { ... } | BraceStmt | declarations.swift:4:11:4:22 | return ... | ReturnStmt | | declarations.swift:4:11:4:22 | return ... | ReturnStmt | declarations.swift:4:18:4:22 | ... call to +(_:_:) ... | BinaryExpr | | declarations.swift:4:18:4:18 | .x | MemberRefExpr | declarations.swift:4:18:4:18 | self | DeclRefExpr | | declarations.swift:4:18:4:22 | ... call to +(_:_:) ... | BinaryExpr | declarations.swift:4:20:4:20 | call to +(_:_:) | DotSyntaxCallExpr | -| declarations.swift:4:20:4:20 | Int.Type | TypeExpr | declarations.swift:4:20:4:20 | FixedTypeRepr | FixedTypeRepr | +| declarations.swift:4:20:4:20 | Int.Type | TypeExpr | declarations.swift:4:20:4:20 | Int | TypeRepr | | declarations.swift:4:20:4:20 | call to +(_:_:) | DotSyntaxCallExpr | declarations.swift:4:20:4:20 | +(_:_:) | DeclRefExpr | | declarations.swift:5:5:5:38 | set | AccessorDecl | declarations.swift:5:9:5:9 | newValue | ParamDecl | | declarations.swift:5:5:5:38 | set | AccessorDecl | declarations.swift:5:19:5:38 | { ... } | BraceStmt | @@ -37,7 +37,7 @@ | declarations.swift:5:21:5:36 | ... = ... | AssignExpr | declarations.swift:5:21:5:21 | .x | MemberRefExpr | | declarations.swift:5:21:5:36 | ... = ... | AssignExpr | declarations.swift:5:25:5:36 | ... call to -(_:_:) ... | BinaryExpr | | declarations.swift:5:25:5:36 | ... call to -(_:_:) ... | BinaryExpr | declarations.swift:5:34:5:34 | call to -(_:_:) | DotSyntaxCallExpr | -| declarations.swift:5:34:5:34 | Int.Type | TypeExpr | declarations.swift:5:34:5:34 | FixedTypeRepr | FixedTypeRepr | +| declarations.swift:5:34:5:34 | Int.Type | TypeExpr | declarations.swift:5:34:5:34 | Int | TypeRepr | | declarations.swift:5:34:5:34 | call to -(_:_:) | DotSyntaxCallExpr | declarations.swift:5:34:5:34 | -(_:_:) | DeclRefExpr | | declarations.swift:9:7:9:7 | deinit | DestructorDecl | declarations.swift:9:7:9:7 | { ... } | BraceStmt | | declarations.swift:9:7:9:7 | init | ConstructorDecl | declarations.swift:9:7:9:7 | { ... } | BraceStmt | @@ -56,7 +56,7 @@ | declarations.swift:9:17:9:17 | { ... } | BraceStmt | file://:0:0:0:0 | ... = ... | AssignExpr | | declarations.swift:9:17:9:17 | { ... } | BraceStmt | file://:0:0:0:0 | return ... | ReturnStmt | | declarations.swift:9:17:9:21 | ... as ... | TypedPattern | declarations.swift:9:17:9:17 | x | NamedPattern | -| declarations.swift:9:17:9:21 | ... as ... | TypedPattern | declarations.swift:9:21:9:21 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| declarations.swift:9:17:9:21 | ... as ... | TypedPattern | declarations.swift:9:21:9:21 | Double | TypeRepr | | declarations.swift:12:5:12:18 | case ... | EnumCaseDecl | declarations.swift:12:10:12:10 | value1 | EnumElementDecl | | declarations.swift:12:5:12:18 | case ... | EnumCaseDecl | declarations.swift:12:18:12:18 | value2 | EnumElementDecl | | declarations.swift:13:5:13:26 | case ... | EnumCaseDecl | declarations.swift:13:10:13:10 | value3 | EnumElementDecl | @@ -75,12 +75,12 @@ | declarations.swift:23:9:23:9 | mustBeSettable | ConcreteVarDecl | declarations.swift:23:31:23:31 | get | AccessorDecl | | declarations.swift:23:9:23:9 | mustBeSettable | ConcreteVarDecl | declarations.swift:23:35:23:35 | set | AccessorDecl | | declarations.swift:23:9:23:25 | ... as ... | TypedPattern | declarations.swift:23:9:23:9 | mustBeSettable | NamedPattern | -| declarations.swift:23:9:23:25 | ... as ... | TypedPattern | declarations.swift:23:25:23:25 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| declarations.swift:23:9:23:25 | ... as ... | TypedPattern | declarations.swift:23:25:23:25 | Int | TypeRepr | | declarations.swift:23:35:23:35 | set | AccessorDecl | declarations.swift:23:35:23:35 | newValue | ParamDecl | | declarations.swift:24:5:24:44 | var ... = ... | PatternBindingDecl | declarations.swift:24:9:24:34 | ... as ... | TypedPattern | | declarations.swift:24:9:24:9 | doesNotNeedToBeSettable | ConcreteVarDecl | declarations.swift:24:40:24:40 | get | AccessorDecl | | declarations.swift:24:9:24:34 | ... as ... | TypedPattern | declarations.swift:24:9:24:9 | doesNotNeedToBeSettable | NamedPattern | -| declarations.swift:24:9:24:34 | ... as ... | TypedPattern | declarations.swift:24:34:24:34 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| declarations.swift:24:9:24:34 | ... as ... | TypedPattern | declarations.swift:24:34:24:34 | Int | TypeRepr | | declarations.swift:28:1:28:37 | a_function(a_parameter:) | ConcreteFuncDecl | declarations.swift:28:17:28:31 | a_parameter | ParamDecl | | declarations.swift:28:1:28:37 | a_function(a_parameter:) | ConcreteFuncDecl | declarations.swift:28:36:28:37 | { ... } | BraceStmt | | declarations.swift:30:1:30:18 | var ... = ... | PatternBindingDecl | declarations.swift:30:5:30:5 | a_variable | NamedPattern | @@ -93,7 +93,7 @@ | declarations.swift:31:5:31:5 | a_property | ConcreteVarDecl | declarations.swift:32:3:34:3 | get | AccessorDecl | | declarations.swift:31:5:31:5 | a_property | ConcreteVarDecl | declarations.swift:35:3:35:18 | set | AccessorDecl | | declarations.swift:31:5:31:18 | ... as ... | TypedPattern | declarations.swift:31:5:31:5 | a_property | NamedPattern | -| declarations.swift:31:5:31:18 | ... as ... | TypedPattern | declarations.swift:31:18:31:18 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| declarations.swift:31:5:31:18 | ... as ... | TypedPattern | declarations.swift:31:18:31:18 | String | TypeRepr | | declarations.swift:32:3:34:3 | get | AccessorDecl | declarations.swift:32:7:34:3 | { ... } | BraceStmt | | declarations.swift:32:7:34:3 | { ... } | BraceStmt | declarations.swift:33:5:33:12 | return ... | ReturnStmt | | declarations.swift:33:5:33:12 | return ... | ReturnStmt | declarations.swift:33:12:33:12 | here | StringLiteralExpr | @@ -118,7 +118,7 @@ | declarations.swift:41:7:41:7 | { ... } | BraceStmt | file://:0:0:0:0 | ... = ... | AssignExpr | | declarations.swift:41:7:41:7 | { ... } | BraceStmt | file://:0:0:0:0 | return ... | ReturnStmt | | declarations.swift:41:7:41:14 | ... as ... | TypedPattern | declarations.swift:41:7:41:7 | field | NamedPattern | -| declarations.swift:41:7:41:14 | ... as ... | TypedPattern | declarations.swift:41:14:41:14 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| declarations.swift:41:7:41:14 | ... as ... | TypedPattern | declarations.swift:41:14:41:14 | Int | TypeRepr | | declarations.swift:42:3:44:3 | init | ConstructorDecl | declarations.swift:42:10:44:3 | { ... } | BraceStmt | | declarations.swift:42:10:44:3 | { ... } | BraceStmt | declarations.swift:43:5:43:13 | ... = ... | AssignExpr | | declarations.swift:42:10:44:3 | { ... } | BraceStmt | declarations.swift:44:3:44:3 | return | ReturnStmt | @@ -139,7 +139,7 @@ | declarations.swift:69:3:73:3 | var ... = ... | PatternBindingDecl | declarations.swift:69:7:69:21 | ... as ... | TypedPattern | | declarations.swift:69:7:69:7 | wrappedValue | ConcreteVarDecl | declarations.swift:70:5:72:5 | get | AccessorDecl | | declarations.swift:69:7:69:21 | ... as ... | TypedPattern | declarations.swift:69:7:69:7 | wrappedValue | NamedPattern | -| declarations.swift:69:7:69:21 | ... as ... | TypedPattern | declarations.swift:69:21:69:21 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| declarations.swift:69:7:69:21 | ... as ... | TypedPattern | declarations.swift:69:21:69:21 | Int | TypeRepr | | declarations.swift:70:5:72:5 | get | AccessorDecl | declarations.swift:70:9:72:5 | { ... } | BraceStmt | | declarations.swift:70:9:72:5 | { ... } | BraceStmt | declarations.swift:71:7:71:14 | return ... | ReturnStmt | | declarations.swift:71:7:71:14 | return ... | ReturnStmt | declarations.swift:71:14:71:14 | 0 | IntegerLiteralExpr | @@ -147,7 +147,7 @@ | declarations.swift:76:19:79:1 | { ... } | BraceStmt | declarations.swift:77:16:77:23 | var ... = ... | PatternBindingDecl | | declarations.swift:76:19:79:1 | { ... } | BraceStmt | declarations.swift:77:20:77:20 | x | ConcreteVarDecl | | declarations.swift:76:19:79:1 | { ... } | BraceStmt | declarations.swift:78:3:78:10 | return ... | ReturnStmt | -| declarations.swift:77:4:77:4 | ZeroWrapper.Type | TypeExpr | declarations.swift:77:4:77:4 | FixedTypeRepr | FixedTypeRepr | +| declarations.swift:77:4:77:4 | ZeroWrapper.Type | TypeExpr | declarations.swift:77:4:77:4 | ZeroWrapper | TypeRepr | | declarations.swift:77:4:77:4 | call to ... | CallExpr | declarations.swift:77:4:77:4 | call to init | ConstructorRefCallExpr | | declarations.swift:77:4:77:4 | call to init | ConstructorRefCallExpr | declarations.swift:77:4:77:4 | init | DeclRefExpr | | declarations.swift:77:16:77:23 | var ... = ... | PatternBindingDecl | declarations.swift:77:20:77:23 | ... as ... | TypedPattern | @@ -156,7 +156,7 @@ | declarations.swift:77:20:77:20 | x | ConcreteVarDecl | declarations.swift:77:20:77:20 | get | AccessorDecl | | declarations.swift:77:20:77:20 | { ... } | BraceStmt | file://:0:0:0:0 | return ... | ReturnStmt | | declarations.swift:77:20:77:23 | ... as ... | TypedPattern | declarations.swift:77:20:77:20 | x | NamedPattern | -| declarations.swift:77:20:77:23 | ... as ... | TypedPattern | declarations.swift:77:23:77:23 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| declarations.swift:77:20:77:23 | ... as ... | TypedPattern | declarations.swift:77:23:77:23 | Int | TypeRepr | | declarations.swift:78:3:78:10 | return ... | ReturnStmt | declarations.swift:78:10:78:10 | x | DeclRefExpr | | declarations.swift:81:8:81:8 | init | ConstructorDecl | declarations.swift:81:8:81:8 | hasBoth | ParamDecl | | declarations.swift:81:8:81:8 | init | ConstructorDecl | declarations.swift:81:8:81:8 | hasDidSet1 | ParamDecl | @@ -172,7 +172,7 @@ | declarations.swift:82:7:82:7 | yield ... | YieldStmt | file://:0:0:0:0 | &... | InOutExpr | | declarations.swift:82:7:82:7 | { ... } | BraceStmt | declarations.swift:82:7:82:7 | yield ... | YieldStmt | | declarations.swift:82:7:82:22 | ... as ... | TypedPattern | declarations.swift:82:7:82:7 | settableField | NamedPattern | -| declarations.swift:82:7:82:22 | ... as ... | TypedPattern | declarations.swift:82:22:82:22 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| declarations.swift:82:7:82:22 | ... as ... | TypedPattern | declarations.swift:82:22:82:22 | Int | TypeRepr | | declarations.swift:83:5:83:11 | set | AccessorDecl | declarations.swift:83:5:83:5 | newValue | ParamDecl | | declarations.swift:83:5:83:11 | set | AccessorDecl | declarations.swift:83:9:83:11 | { ... } | BraceStmt | | declarations.swift:84:5:86:5 | get | AccessorDecl | declarations.swift:84:9:86:5 | { ... } | BraceStmt | @@ -181,14 +181,14 @@ | declarations.swift:91:3:93:3 | var ... = ... | PatternBindingDecl | declarations.swift:91:7:91:23 | ... as ... | TypedPattern | | declarations.swift:91:7:91:7 | readOnlyField1 | ConcreteVarDecl | declarations.swift:91:27:93:3 | get | AccessorDecl | | declarations.swift:91:7:91:23 | ... as ... | TypedPattern | declarations.swift:91:7:91:7 | readOnlyField1 | NamedPattern | -| declarations.swift:91:7:91:23 | ... as ... | TypedPattern | declarations.swift:91:23:91:23 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| declarations.swift:91:7:91:23 | ... as ... | TypedPattern | declarations.swift:91:23:91:23 | Int | TypeRepr | | declarations.swift:91:27:93:3 | get | AccessorDecl | declarations.swift:91:27:93:3 | { ... } | BraceStmt | | declarations.swift:91:27:93:3 | { ... } | BraceStmt | declarations.swift:92:5:92:12 | return ... | ReturnStmt | | declarations.swift:92:5:92:12 | return ... | ReturnStmt | declarations.swift:92:12:92:12 | 0 | IntegerLiteralExpr | | declarations.swift:96:3:100:3 | var ... = ... | PatternBindingDecl | declarations.swift:96:7:96:23 | ... as ... | TypedPattern | | declarations.swift:96:7:96:7 | readOnlyField2 | ConcreteVarDecl | declarations.swift:97:5:99:5 | get | AccessorDecl | | declarations.swift:96:7:96:23 | ... as ... | TypedPattern | declarations.swift:96:7:96:7 | readOnlyField2 | NamedPattern | -| declarations.swift:96:7:96:23 | ... as ... | TypedPattern | declarations.swift:96:23:96:23 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| declarations.swift:96:7:96:23 | ... as ... | TypedPattern | declarations.swift:96:23:96:23 | Int | TypeRepr | | declarations.swift:97:5:99:5 | get | AccessorDecl | declarations.swift:97:9:99:5 | { ... } | BraceStmt | | declarations.swift:97:9:99:5 | { ... } | BraceStmt | declarations.swift:98:7:98:14 | return ... | ReturnStmt | | declarations.swift:98:7:98:14 | return ... | ReturnStmt | declarations.swift:98:14:98:14 | 0 | IntegerLiteralExpr | @@ -205,7 +205,7 @@ | declarations.swift:102:7:102:7 | { ... } | BraceStmt | file://:0:0:0:0 | ... = ... | AssignExpr | | declarations.swift:102:7:102:7 | { ... } | BraceStmt | file://:0:0:0:0 | return ... | ReturnStmt | | declarations.swift:102:7:102:21 | ... as ... | TypedPattern | declarations.swift:102:7:102:7 | normalField | NamedPattern | -| declarations.swift:102:7:102:21 | ... as ... | TypedPattern | declarations.swift:102:21:102:21 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| declarations.swift:102:7:102:21 | ... as ... | TypedPattern | declarations.swift:102:21:102:21 | Int | TypeRepr | | declarations.swift:104:3:104:3 | (unnamed function decl) | AccessorDecl | declarations.swift:104:3:104:3 | { ... } | BraceStmt | | declarations.swift:104:3:104:3 | (unnamed function decl) | AccessorDecl | file://:0:0:0:0 | x | ParamDecl | | declarations.swift:104:3:104:3 | yield ... | YieldStmt | file://:0:0:0:0 | &... | InOutExpr | @@ -244,7 +244,7 @@ | declarations.swift:115:7:115:7 | { ... } | BraceStmt | file://:0:0:0:0 | call to ... | CallExpr | | declarations.swift:115:7:115:7 | { ... } | BraceStmt | file://:0:0:0:0 | return ... | ReturnStmt | | declarations.swift:115:7:115:21 | ... as ... | TypedPattern | declarations.swift:115:7:115:7 | hasWillSet1 | NamedPattern | -| declarations.swift:115:7:115:21 | ... as ... | TypedPattern | declarations.swift:115:21:115:21 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| declarations.swift:115:7:115:21 | ... as ... | TypedPattern | declarations.swift:115:21:115:21 | Int | TypeRepr | | declarations.swift:116:5:116:25 | willSet | AccessorDecl | declarations.swift:116:13:116:13 | newValue | ParamDecl | | declarations.swift:116:5:116:25 | willSet | AccessorDecl | declarations.swift:116:23:116:25 | { ... } | BraceStmt | | declarations.swift:119:3:121:3 | var ... = ... | PatternBindingDecl | declarations.swift:119:7:119:21 | ... as ... | TypedPattern | @@ -262,7 +262,7 @@ | declarations.swift:119:7:119:7 | { ... } | BraceStmt | file://:0:0:0:0 | call to ... | CallExpr | | declarations.swift:119:7:119:7 | { ... } | BraceStmt | file://:0:0:0:0 | return ... | ReturnStmt | | declarations.swift:119:7:119:21 | ... as ... | TypedPattern | declarations.swift:119:7:119:7 | hasWillSet2 | NamedPattern | -| declarations.swift:119:7:119:21 | ... as ... | TypedPattern | declarations.swift:119:21:119:21 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| declarations.swift:119:7:119:21 | ... as ... | TypedPattern | declarations.swift:119:21:119:21 | Int | TypeRepr | | declarations.swift:120:5:120:15 | willSet | AccessorDecl | declarations.swift:120:5:120:5 | newValue | ParamDecl | | declarations.swift:120:5:120:15 | willSet | AccessorDecl | declarations.swift:120:13:120:15 | { ... } | BraceStmt | | declarations.swift:123:3:125:3 | var ... = ... | PatternBindingDecl | declarations.swift:123:7:123:20 | ... as ... | TypedPattern | @@ -282,7 +282,7 @@ | declarations.swift:123:7:123:7 | { ... } | BraceStmt | file://:0:0:0:0 | tmp | ConcreteVarDecl | | declarations.swift:123:7:123:7 | { ... } | BraceStmt | file://:0:0:0:0 | var ... = ... | PatternBindingDecl | | declarations.swift:123:7:123:20 | ... as ... | TypedPattern | declarations.swift:123:7:123:7 | hasDidSet1 | NamedPattern | -| declarations.swift:123:7:123:20 | ... as ... | TypedPattern | declarations.swift:123:20:123:20 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| declarations.swift:123:7:123:20 | ... as ... | TypedPattern | declarations.swift:123:20:123:20 | Int | TypeRepr | | declarations.swift:124:5:124:24 | didSet | AccessorDecl | declarations.swift:124:12:124:12 | oldValue | ParamDecl | | declarations.swift:124:5:124:24 | didSet | AccessorDecl | declarations.swift:124:22:124:24 | { ... } | BraceStmt | | declarations.swift:127:3:129:3 | var ... = ... | PatternBindingDecl | declarations.swift:127:7:127:20 | ... as ... | TypedPattern | @@ -301,7 +301,7 @@ | declarations.swift:127:7:127:7 | { ... } | BraceStmt | file://:0:0:0:0 | call to ... | CallExpr | | declarations.swift:127:7:127:7 | { ... } | BraceStmt | file://:0:0:0:0 | return ... | ReturnStmt | | declarations.swift:127:7:127:20 | ... as ... | TypedPattern | declarations.swift:127:7:127:7 | hasDidSet2 | NamedPattern | -| declarations.swift:127:7:127:20 | ... as ... | TypedPattern | declarations.swift:127:20:127:20 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| declarations.swift:127:7:127:20 | ... as ... | TypedPattern | declarations.swift:127:20:127:20 | Int | TypeRepr | | declarations.swift:128:5:128:14 | didSet | AccessorDecl | declarations.swift:128:12:128:14 | { ... } | BraceStmt | | declarations.swift:131:3:135:3 | var ... = ... | PatternBindingDecl | declarations.swift:131:7:131:17 | ... as ... | TypedPattern | | declarations.swift:131:7:131:7 | (unnamed function decl) | AccessorDecl | declarations.swift:131:7:131:7 | { ... } | BraceStmt | @@ -320,7 +320,7 @@ | declarations.swift:131:7:131:7 | { ... } | BraceStmt | file://:0:0:0:0 | call to ... | CallExpr | | declarations.swift:131:7:131:7 | { ... } | BraceStmt | file://:0:0:0:0 | return ... | ReturnStmt | | declarations.swift:131:7:131:17 | ... as ... | TypedPattern | declarations.swift:131:7:131:7 | hasBoth | NamedPattern | -| declarations.swift:131:7:131:17 | ... as ... | TypedPattern | declarations.swift:131:17:131:17 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| declarations.swift:131:7:131:17 | ... as ... | TypedPattern | declarations.swift:131:17:131:17 | Int | TypeRepr | | declarations.swift:132:5:132:15 | willSet | AccessorDecl | declarations.swift:132:5:132:5 | newValue | ParamDecl | | declarations.swift:132:5:132:15 | willSet | AccessorDecl | declarations.swift:132:13:132:15 | { ... } | BraceStmt | | declarations.swift:134:5:134:14 | didSet | AccessorDecl | declarations.swift:134:12:134:14 | { ... } | BraceStmt | @@ -382,7 +382,7 @@ | expressions.swift:8:1:8:15 | { ... } | BraceStmt | expressions.swift:8:1:8:15 | var ... = ... | PatternBindingDecl | | expressions.swift:8:1:8:15 | { ... } | TopLevelCodeDecl | expressions.swift:8:1:8:15 | { ... } | BraceStmt | | expressions.swift:8:5:8:11 | ... as ... | TypedPattern | expressions.swift:8:5:8:5 | n | NamedPattern | -| expressions.swift:8:5:8:11 | ... as ... | TypedPattern | expressions.swift:8:8:8:11 | ...? | OptionalTypeRepr | +| expressions.swift:8:5:8:11 | ... as ... | TypedPattern | expressions.swift:8:8:8:11 | Int? | TypeRepr | | expressions.swift:11:3:11:8 | case ... | EnumCaseDecl | expressions.swift:11:8:11:8 | failed | EnumElementDecl | | expressions.swift:14:1:18:1 | failure(_:) | ConcreteFuncDecl | expressions.swift:14:14:14:19 | x | ParamDecl | | expressions.swift:14:1:18:1 | failure(_:) | ConcreteFuncDecl | expressions.swift:14:31:18:1 | { ... } | BraceStmt | @@ -390,11 +390,11 @@ | expressions.swift:15:3:17:3 | guard ... else { ... } | GuardStmt | expressions.swift:15:9:15:14 | StmtCondition | StmtCondition | | expressions.swift:15:3:17:3 | guard ... else { ... } | GuardStmt | expressions.swift:15:21:17:3 | { ... } | BraceStmt | | expressions.swift:15:9:15:14 | ... call to !=(_:_:) ... | BinaryExpr | expressions.swift:15:11:15:11 | call to !=(_:_:) | DotSyntaxCallExpr | -| expressions.swift:15:11:15:11 | Int.Type | TypeExpr | expressions.swift:15:11:15:11 | FixedTypeRepr | FixedTypeRepr | +| expressions.swift:15:11:15:11 | Int.Type | TypeExpr | expressions.swift:15:11:15:11 | Int | TypeRepr | | expressions.swift:15:11:15:11 | call to !=(_:_:) | DotSyntaxCallExpr | expressions.swift:15:11:15:11 | !=(_:_:) | DeclRefExpr | | expressions.swift:15:21:17:3 | { ... } | BraceStmt | expressions.swift:16:5:16:19 | throw ... | ThrowStmt | | expressions.swift:16:5:16:19 | throw ... | ThrowStmt | expressions.swift:16:11:16:19 | (Error) ... | ErasureExpr | -| expressions.swift:16:11:16:11 | AnError.Type | TypeExpr | expressions.swift:16:11:16:11 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| expressions.swift:16:11:16:11 | AnError.Type | TypeExpr | expressions.swift:16:11:16:11 | AnError | TypeRepr | | expressions.swift:16:11:16:19 | (Error) ... | ErasureExpr | expressions.swift:16:11:16:19 | call to ... | DotSyntaxCallExpr | | expressions.swift:16:11:16:19 | call to ... | DotSyntaxCallExpr | expressions.swift:16:19:16:19 | failed | DeclRefExpr | | expressions.swift:20:1:20:16 | try! ... | ForceTryExpr | expressions.swift:20:6:20:16 | call to failure(_:) | CallExpr | @@ -413,7 +413,7 @@ | expressions.swift:27:1:27:19 | var ... = ... | PatternBindingDecl | expressions.swift:27:13:27:19 | call to ... | CallExpr | | expressions.swift:27:1:27:19 | { ... } | BraceStmt | expressions.swift:27:1:27:19 | var ... = ... | PatternBindingDecl | | expressions.swift:27:1:27:19 | { ... } | TopLevelCodeDecl | expressions.swift:27:1:27:19 | { ... } | BraceStmt | -| expressions.swift:27:13:27:13 | Klass.Type | TypeExpr | expressions.swift:27:13:27:13 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| expressions.swift:27:13:27:13 | Klass.Type | TypeExpr | expressions.swift:27:13:27:13 | Klass | TypeRepr | | expressions.swift:27:13:27:13 | call to init | ConstructorRefCallExpr | expressions.swift:27:13:27:13 | init | DeclRefExpr | | expressions.swift:27:13:27:19 | call to ... | CallExpr | expressions.swift:27:13:27:13 | call to init | ConstructorRefCallExpr | | expressions.swift:29:1:29:19 | var ... = ... | PatternBindingDecl | expressions.swift:29:5:29:5 | d | NamedPattern | @@ -467,7 +467,7 @@ | expressions.swift:41:10:43:1 | { ... } | ClosureExpr | expressions.swift:41:21:41:24 | y | ParamDecl | | expressions.swift:42:5:42:16 | return ... | ReturnStmt | expressions.swift:42:12:42:16 | ... call to +(_:_:) ... | BinaryExpr | | expressions.swift:42:12:42:16 | ... call to +(_:_:) ... | BinaryExpr | expressions.swift:42:14:42:14 | call to +(_:_:) | DotSyntaxCallExpr | -| expressions.swift:42:14:42:14 | Int.Type | TypeExpr | expressions.swift:42:14:42:14 | FixedTypeRepr | FixedTypeRepr | +| expressions.swift:42:14:42:14 | Int.Type | TypeExpr | expressions.swift:42:14:42:14 | Int | TypeRepr | | expressions.swift:42:14:42:14 | call to +(_:_:) | DotSyntaxCallExpr | expressions.swift:42:14:42:14 | +(_:_:) | DeclRefExpr | | expressions.swift:44:1:46:1 | call to closured(closure:) | CallExpr | expressions.swift:44:1:44:1 | closured(closure:) | DeclRefExpr | | expressions.swift:44:1:46:1 | { ... } | BraceStmt | expressions.swift:44:1:46:1 | call to closured(closure:) | CallExpr | @@ -478,7 +478,7 @@ | expressions.swift:44:10:46:1 | { ... } | ClosureExpr | expressions.swift:44:15:44:15 | y | ParamDecl | | expressions.swift:45:5:45:16 | return ... | ReturnStmt | expressions.swift:45:12:45:16 | ... call to +(_:_:) ... | BinaryExpr | | expressions.swift:45:12:45:16 | ... call to +(_:_:) ... | BinaryExpr | expressions.swift:45:14:45:14 | call to +(_:_:) | DotSyntaxCallExpr | -| expressions.swift:45:14:45:14 | Int.Type | TypeExpr | expressions.swift:45:14:45:14 | FixedTypeRepr | FixedTypeRepr | +| expressions.swift:45:14:45:14 | Int.Type | TypeExpr | expressions.swift:45:14:45:14 | Int | TypeRepr | | expressions.swift:45:14:45:14 | call to +(_:_:) | DotSyntaxCallExpr | expressions.swift:45:14:45:14 | +(_:_:) | DeclRefExpr | | expressions.swift:47:1:47:27 | call to closured(closure:) | CallExpr | expressions.swift:47:1:47:1 | closured(closure:) | DeclRefExpr | | expressions.swift:47:1:47:27 | { ... } | BraceStmt | expressions.swift:47:1:47:27 | call to closured(closure:) | CallExpr | @@ -489,7 +489,7 @@ | expressions.swift:47:10:47:27 | { ... } | ClosureExpr | expressions.swift:47:10:47:27 | { ... } | BraceStmt | | expressions.swift:47:12:47:24 | return ... | ReturnStmt | expressions.swift:47:19:47:24 | ... call to +(_:_:) ... | BinaryExpr | | expressions.swift:47:19:47:24 | ... call to +(_:_:) ... | BinaryExpr | expressions.swift:47:22:47:22 | call to +(_:_:) | DotSyntaxCallExpr | -| expressions.swift:47:22:47:22 | Int.Type | TypeExpr | expressions.swift:47:22:47:22 | FixedTypeRepr | FixedTypeRepr | +| expressions.swift:47:22:47:22 | Int.Type | TypeExpr | expressions.swift:47:22:47:22 | Int | TypeRepr | | expressions.swift:47:22:47:22 | call to +(_:_:) | DotSyntaxCallExpr | expressions.swift:47:22:47:22 | +(_:_:) | DeclRefExpr | | expressions.swift:48:1:48:20 | call to closured(closure:) | CallExpr | expressions.swift:48:1:48:1 | closured(closure:) | DeclRefExpr | | expressions.swift:48:1:48:20 | { ... } | BraceStmt | expressions.swift:48:1:48:20 | call to closured(closure:) | CallExpr | @@ -500,7 +500,7 @@ | expressions.swift:48:10:48:20 | { ... } | ClosureExpr | expressions.swift:48:10:48:20 | { ... } | BraceStmt | | expressions.swift:48:12:48:17 | ... call to +(_:_:) ... | BinaryExpr | expressions.swift:48:15:48:15 | call to +(_:_:) | DotSyntaxCallExpr | | expressions.swift:48:12:48:17 | return ... | ReturnStmt | expressions.swift:48:12:48:17 | ... call to +(_:_:) ... | BinaryExpr | -| expressions.swift:48:15:48:15 | Int.Type | TypeExpr | expressions.swift:48:15:48:15 | FixedTypeRepr | FixedTypeRepr | +| expressions.swift:48:15:48:15 | Int.Type | TypeExpr | expressions.swift:48:15:48:15 | Int | TypeRepr | | expressions.swift:48:15:48:15 | call to +(_:_:) | DotSyntaxCallExpr | expressions.swift:48:15:48:15 | +(_:_:) | DeclRefExpr | | expressions.swift:50:8:50:8 | init | ConstructorDecl | expressions.swift:50:8:50:8 | x | ParamDecl | | expressions.swift:51:3:51:10 | var ... = ... | PatternBindingDecl | expressions.swift:51:7:51:10 | ... as ... | TypedPattern | @@ -508,14 +508,12 @@ | expressions.swift:51:7:51:7 | x | ConcreteVarDecl | expressions.swift:51:7:51:7 | get | AccessorDecl | | expressions.swift:51:7:51:7 | { ... } | BraceStmt | file://:0:0:0:0 | return ... | ReturnStmt | | expressions.swift:51:7:51:10 | ... as ... | TypedPattern | expressions.swift:51:7:51:7 | x | NamedPattern | -| expressions.swift:51:7:51:10 | ... as ... | TypedPattern | expressions.swift:51:10:51:10 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| expressions.swift:51:7:51:10 | ... as ... | TypedPattern | expressions.swift:51:10:51:10 | Int | TypeRepr | | expressions.swift:54:1:54:8 | ... = ... | AssignExpr | expressions.swift:54:1:54:1 | _ | DiscardAssignmentExpr | | expressions.swift:54:1:54:8 | ... = ... | AssignExpr | expressions.swift:54:5:54:8 | #keyPath(...) | KeyPathExpr | | expressions.swift:54:1:54:8 | { ... } | BraceStmt | expressions.swift:54:1:54:8 | ... = ... | AssignExpr | | expressions.swift:54:1:54:8 | { ... } | TopLevelCodeDecl | expressions.swift:54:1:54:8 | { ... } | BraceStmt | -| expressions.swift:54:5:54:8 | #keyPath(...) | KeyPathExpr | expressions.swift:54:6:54:8 | ... .x | UnresolvedDotExpr | -| expressions.swift:54:6:54:6 | (no string representation) | TypeExpr | expressions.swift:54:6:54:6 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | -| expressions.swift:54:6:54:8 | ... .x | UnresolvedDotExpr | expressions.swift:54:6:54:6 | (no string representation) | TypeExpr | +| expressions.swift:54:5:54:8 | #keyPath(...) | KeyPathExpr | expressions.swift:54:6:54:6 | S | TypeRepr | | expressions.swift:56:1:57:1 | unsafeFunction(pointer:) | ConcreteFuncDecl | expressions.swift:56:21:56:47 | pointer | ParamDecl | | expressions.swift:56:1:57:1 | unsafeFunction(pointer:) | ConcreteFuncDecl | expressions.swift:56:50:57:1 | { ... } | BraceStmt | | expressions.swift:58:1:58:16 | var ... = ... | PatternBindingDecl | expressions.swift:58:5:58:5 | myNumber | NamedPattern | @@ -545,7 +543,7 @@ | expressions.swift:64:5:66:5 | if ... then { ... } | IfStmt | expressions.swift:64:8:64:12 | StmtCondition | StmtCondition | | expressions.swift:64:5:66:5 | if ... then { ... } | IfStmt | expressions.swift:64:14:66:5 | { ... } | BraceStmt | | expressions.swift:64:8:64:12 | ... call to <(_:_:) ... | BinaryExpr | expressions.swift:64:10:64:10 | call to <(_:_:) | DotSyntaxCallExpr | -| expressions.swift:64:10:64:10 | Int.Type | TypeExpr | expressions.swift:64:10:64:10 | FixedTypeRepr | FixedTypeRepr | +| expressions.swift:64:10:64:10 | Int.Type | TypeExpr | expressions.swift:64:10:64:10 | Int | TypeRepr | | expressions.swift:64:10:64:10 | call to <(_:_:) | DotSyntaxCallExpr | expressions.swift:64:10:64:10 | <(_:_:) | DeclRefExpr | | expressions.swift:64:14:66:5 | { ... } | BraceStmt | expressions.swift:65:7:65:14 | fail | FailStmt | | expressions.swift:70:7:70:7 | deinit | DestructorDecl | expressions.swift:70:7:70:7 | { ... } | BraceStmt | @@ -554,7 +552,7 @@ | expressions.swift:71:7:71:7 | xx | ConcreteVarDecl | expressions.swift:71:7:71:7 | get | AccessorDecl | | expressions.swift:71:7:71:7 | { ... } | BraceStmt | file://:0:0:0:0 | return ... | ReturnStmt | | expressions.swift:71:7:71:11 | ... as ... | TypedPattern | expressions.swift:71:7:71:7 | xx | NamedPattern | -| expressions.swift:71:7:71:11 | ... as ... | TypedPattern | expressions.swift:71:11:71:11 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| expressions.swift:71:7:71:11 | ... as ... | TypedPattern | expressions.swift:71:11:71:11 | Int | TypeRepr | | expressions.swift:72:3:74:3 | init | ConstructorDecl | expressions.swift:72:8:72:11 | x | ParamDecl | | expressions.swift:72:3:74:3 | init | ConstructorDecl | expressions.swift:72:16:74:3 | { ... } | BraceStmt | | expressions.swift:72:16:74:3 | { ... } | BraceStmt | expressions.swift:73:5:73:10 | ... = ... | AssignExpr | @@ -577,7 +575,7 @@ | expressions.swift:83:1:83:23 | var ... = ... | PatternBindingDecl | expressions.swift:83:15:83:23 | call to ... | CallExpr | | expressions.swift:83:1:83:23 | { ... } | BraceStmt | expressions.swift:83:1:83:23 | var ... = ... | PatternBindingDecl | | expressions.swift:83:1:83:23 | { ... } | TopLevelCodeDecl | expressions.swift:83:1:83:23 | { ... } | BraceStmt | -| expressions.swift:83:15:83:15 | Derived.Type | TypeExpr | expressions.swift:83:15:83:15 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| expressions.swift:83:15:83:15 | Derived.Type | TypeExpr | expressions.swift:83:15:83:15 | Derived | TypeRepr | | expressions.swift:83:15:83:15 | call to init | ConstructorRefCallExpr | expressions.swift:83:15:83:15 | init | DeclRefExpr | | expressions.swift:83:15:83:23 | call to ... | CallExpr | expressions.swift:83:15:83:15 | call to init | ConstructorRefCallExpr | | expressions.swift:84:1:84:13 | ... = ... | AssignExpr | expressions.swift:84:1:84:1 | _ | DiscardAssignmentExpr | @@ -591,7 +589,7 @@ | expressions.swift:86:1:86:13 | { ... } | BraceStmt | expressions.swift:86:1:86:13 | var ... = ... | PatternBindingDecl | | expressions.swift:86:1:86:13 | { ... } | TopLevelCodeDecl | expressions.swift:86:1:86:13 | { ... } | BraceStmt | | expressions.swift:86:5:86:13 | ... as ... | TypedPattern | expressions.swift:86:5:86:5 | opt | NamedPattern | -| expressions.swift:86:5:86:13 | ... as ... | TypedPattern | expressions.swift:86:10:86:13 | ...? | OptionalTypeRepr | +| expressions.swift:86:5:86:13 | ... as ... | TypedPattern | expressions.swift:86:10:86:13 | Int? | TypeRepr | | expressions.swift:87:1:87:4 | ...! | ForceValueExpr | expressions.swift:87:1:87:1 | opt | DeclRefExpr | | expressions.swift:87:1:87:4 | { ... } | BraceStmt | expressions.swift:87:1:87:4 | ...! | ForceValueExpr | | expressions.swift:87:1:87:4 | { ... } | TopLevelCodeDecl | expressions.swift:87:1:87:4 | { ... } | BraceStmt | @@ -606,15 +604,15 @@ | expressions.swift:92:1:92:55 | var ... = ... | PatternBindingDecl | expressions.swift:92:14:92:55 | call to ... | CallExpr | | expressions.swift:92:1:92:55 | { ... } | BraceStmt | expressions.swift:92:1:92:55 | var ... = ... | PatternBindingDecl | | expressions.swift:92:1:92:55 | { ... } | TopLevelCodeDecl | expressions.swift:92:1:92:55 | { ... } | BraceStmt | -| expressions.swift:92:14:92:14 | Unmanaged.Type | TypeExpr | expressions.swift:92:14:92:14 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| expressions.swift:92:14:92:14 | Unmanaged.Type | TypeExpr | expressions.swift:92:14:92:14 | Unmanaged | TypeRepr | | expressions.swift:92:14:92:24 | call to passRetained(_:) | DotSyntaxCallExpr | expressions.swift:92:24:92:24 | passRetained(_:) | DeclRefExpr | | expressions.swift:92:14:92:44 | call to ... | CallExpr | expressions.swift:92:14:92:24 | call to passRetained(_:) | DotSyntaxCallExpr | | expressions.swift:92:14:92:46 | call to toOpaque() | DotSyntaxCallExpr | expressions.swift:92:46:92:46 | toOpaque() | DeclRefExpr | | expressions.swift:92:14:92:55 | call to ... | CallExpr | expressions.swift:92:14:92:46 | call to toOpaque() | DotSyntaxCallExpr | -| expressions.swift:92:37:92:37 | ToPtr.Type | TypeExpr | expressions.swift:92:37:92:37 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| expressions.swift:92:37:92:37 | ToPtr.Type | TypeExpr | expressions.swift:92:37:92:37 | ToPtr | TypeRepr | | expressions.swift:92:37:92:37 | call to init | ConstructorRefCallExpr | expressions.swift:92:37:92:37 | init | DeclRefExpr | | expressions.swift:92:37:92:43 | call to ... | CallExpr | expressions.swift:92:37:92:37 | call to init | ConstructorRefCallExpr | -| expressions.swift:93:1:93:16 | Unmanaged.Type | TypeExpr | expressions.swift:93:1:93:16 | ...<...> | GenericIdentTypeRepr | +| expressions.swift:93:1:93:16 | Unmanaged.Type | TypeExpr | expressions.swift:93:1:93:16 | Unmanaged | TypeRepr | | expressions.swift:93:1:93:18 | call to fromOpaque(_:) | DotSyntaxCallExpr | expressions.swift:93:18:93:18 | fromOpaque(_:) | DeclRefExpr | | expressions.swift:93:1:93:35 | call to ... | CallExpr | expressions.swift:93:1:93:18 | call to fromOpaque(_:) | DotSyntaxCallExpr | | expressions.swift:93:1:93:35 | { ... } | BraceStmt | expressions.swift:93:1:93:35 | call to ... | CallExpr | @@ -629,7 +627,7 @@ | expressions.swift:96:7:96:7 | yield ... | YieldStmt | file://:0:0:0:0 | &... | InOutExpr | | expressions.swift:96:7:96:7 | { ... } | BraceStmt | expressions.swift:96:7:96:7 | yield ... | YieldStmt | | expressions.swift:96:7:96:22 | ... as ... | TypedPattern | expressions.swift:96:7:96:7 | settableField | NamedPattern | -| expressions.swift:96:7:96:22 | ... as ... | TypedPattern | expressions.swift:96:22:96:22 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| expressions.swift:96:7:96:22 | ... as ... | TypedPattern | expressions.swift:96:22:96:22 | Int | TypeRepr | | expressions.swift:97:5:97:11 | set | AccessorDecl | expressions.swift:97:5:97:5 | newValue | ParamDecl | | expressions.swift:97:5:97:11 | set | AccessorDecl | expressions.swift:97:9:97:11 | { ... } | BraceStmt | | expressions.swift:98:5:100:5 | get | AccessorDecl | expressions.swift:98:9:100:5 | { ... } | BraceStmt | @@ -638,14 +636,14 @@ | expressions.swift:105:3:107:3 | var ... = ... | PatternBindingDecl | expressions.swift:105:7:105:23 | ... as ... | TypedPattern | | expressions.swift:105:7:105:7 | readOnlyField1 | ConcreteVarDecl | expressions.swift:105:27:107:3 | get | AccessorDecl | | expressions.swift:105:7:105:23 | ... as ... | TypedPattern | expressions.swift:105:7:105:7 | readOnlyField1 | NamedPattern | -| expressions.swift:105:7:105:23 | ... as ... | TypedPattern | expressions.swift:105:23:105:23 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| expressions.swift:105:7:105:23 | ... as ... | TypedPattern | expressions.swift:105:23:105:23 | Int | TypeRepr | | expressions.swift:105:27:107:3 | get | AccessorDecl | expressions.swift:105:27:107:3 | { ... } | BraceStmt | | expressions.swift:105:27:107:3 | { ... } | BraceStmt | expressions.swift:106:5:106:12 | return ... | ReturnStmt | | expressions.swift:106:5:106:12 | return ... | ReturnStmt | expressions.swift:106:12:106:12 | 0 | IntegerLiteralExpr | | expressions.swift:110:3:114:3 | var ... = ... | PatternBindingDecl | expressions.swift:110:7:110:23 | ... as ... | TypedPattern | | expressions.swift:110:7:110:7 | readOnlyField2 | ConcreteVarDecl | expressions.swift:111:5:113:5 | get | AccessorDecl | | expressions.swift:110:7:110:23 | ... as ... | TypedPattern | expressions.swift:110:7:110:7 | readOnlyField2 | NamedPattern | -| expressions.swift:110:7:110:23 | ... as ... | TypedPattern | expressions.swift:110:23:110:23 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| expressions.swift:110:7:110:23 | ... as ... | TypedPattern | expressions.swift:110:23:110:23 | Int | TypeRepr | | expressions.swift:111:5:113:5 | get | AccessorDecl | expressions.swift:111:9:113:5 | { ... } | BraceStmt | | expressions.swift:111:9:113:5 | { ... } | BraceStmt | expressions.swift:112:7:112:14 | return ... | ReturnStmt | | expressions.swift:112:7:112:14 | return ... | ReturnStmt | expressions.swift:112:14:112:14 | 0 | IntegerLiteralExpr | @@ -662,7 +660,7 @@ | expressions.swift:116:7:116:7 | { ... } | BraceStmt | file://:0:0:0:0 | ... = ... | AssignExpr | | expressions.swift:116:7:116:7 | { ... } | BraceStmt | file://:0:0:0:0 | return ... | ReturnStmt | | expressions.swift:116:7:116:21 | ... as ... | TypedPattern | expressions.swift:116:7:116:7 | normalField | NamedPattern | -| expressions.swift:116:7:116:21 | ... as ... | TypedPattern | expressions.swift:116:21:116:21 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| expressions.swift:116:7:116:21 | ... as ... | TypedPattern | expressions.swift:116:21:116:21 | Int | TypeRepr | | expressions.swift:118:3:118:3 | (unnamed function decl) | AccessorDecl | expressions.swift:118:3:118:3 | { ... } | BraceStmt | | expressions.swift:118:3:118:3 | (unnamed function decl) | AccessorDecl | file://:0:0:0:0 | x | ParamDecl | | expressions.swift:118:3:118:3 | yield ... | YieldStmt | file://:0:0:0:0 | &... | InOutExpr | @@ -743,7 +741,7 @@ | expressions.swift:142:7:142:7 | { ... } | BraceStmt | file://:0:0:0:0 | ... = ... | AssignExpr | | expressions.swift:142:7:142:7 | { ... } | BraceStmt | file://:0:0:0:0 | return ... | ReturnStmt | | expressions.swift:142:7:142:11 | ... as ... | TypedPattern | expressions.swift:142:7:142:7 | x | NamedPattern | -| expressions.swift:142:7:142:11 | ... as ... | TypedPattern | expressions.swift:142:11:142:11 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| expressions.swift:142:7:142:11 | ... as ... | TypedPattern | expressions.swift:142:11:142:11 | Int | TypeRepr | | expressions.swift:145:8:145:8 | init | ConstructorDecl | expressions.swift:145:8:145:8 | b | ParamDecl | | expressions.swift:145:8:145:8 | init | ConstructorDecl | expressions.swift:145:8:145:8 | bs | ParamDecl | | expressions.swift:145:8:145:8 | init | ConstructorDecl | expressions.swift:145:8:145:8 | mayB | ParamDecl | @@ -760,7 +758,7 @@ | expressions.swift:146:7:146:7 | { ... } | BraceStmt | file://:0:0:0:0 | ... = ... | AssignExpr | | expressions.swift:146:7:146:7 | { ... } | BraceStmt | file://:0:0:0:0 | return ... | ReturnStmt | | expressions.swift:146:7:146:11 | ... as ... | TypedPattern | expressions.swift:146:7:146:7 | b | NamedPattern | -| expressions.swift:146:7:146:11 | ... as ... | TypedPattern | expressions.swift:146:11:146:11 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| expressions.swift:146:7:146:11 | ... as ... | TypedPattern | expressions.swift:146:11:146:11 | B | TypeRepr | | expressions.swift:147:3:147:14 | var ... = ... | PatternBindingDecl | expressions.swift:147:7:147:14 | ... as ... | TypedPattern | | expressions.swift:147:7:147:7 | (unnamed function decl) | AccessorDecl | expressions.swift:147:7:147:7 | { ... } | BraceStmt | | expressions.swift:147:7:147:7 | bs | ConcreteVarDecl | expressions.swift:147:7:147:7 | (unnamed function decl) | AccessorDecl | @@ -774,7 +772,7 @@ | expressions.swift:147:7:147:7 | { ... } | BraceStmt | file://:0:0:0:0 | ... = ... | AssignExpr | | expressions.swift:147:7:147:7 | { ... } | BraceStmt | file://:0:0:0:0 | return ... | ReturnStmt | | expressions.swift:147:7:147:14 | ... as ... | TypedPattern | expressions.swift:147:7:147:7 | bs | NamedPattern | -| expressions.swift:147:7:147:14 | ... as ... | TypedPattern | expressions.swift:147:12:147:14 | [...] | ArrayTypeRepr | +| expressions.swift:147:7:147:14 | ... as ... | TypedPattern | expressions.swift:147:12:147:14 | [B] | TypeRepr | | expressions.swift:148:3:148:15 | var ... = ... | PatternBindingDecl | expressions.swift:148:7:148:15 | ... as ... | TypedPattern | | expressions.swift:148:3:148:15 | var ... = ... | PatternBindingDecl | file://:0:0:0:0 | nil | NilLiteralExpr | | expressions.swift:148:7:148:7 | (unnamed function decl) | AccessorDecl | expressions.swift:148:7:148:7 | { ... } | BraceStmt | @@ -789,7 +787,7 @@ | expressions.swift:148:7:148:7 | { ... } | BraceStmt | file://:0:0:0:0 | ... = ... | AssignExpr | | expressions.swift:148:7:148:7 | { ... } | BraceStmt | file://:0:0:0:0 | return ... | ReturnStmt | | expressions.swift:148:7:148:15 | ... as ... | TypedPattern | expressions.swift:148:7:148:7 | mayB | NamedPattern | -| expressions.swift:148:7:148:15 | ... as ... | TypedPattern | expressions.swift:148:14:148:15 | ...? | OptionalTypeRepr | +| expressions.swift:148:7:148:15 | ... as ... | TypedPattern | expressions.swift:148:14:148:15 | B? | TypeRepr | | expressions.swift:151:1:155:1 | test(a:keyPathInt:keyPathB:) | ConcreteFuncDecl | expressions.swift:151:11:151:15 | a | ParamDecl | | expressions.swift:151:1:155:1 | test(a:keyPathInt:keyPathB:) | ConcreteFuncDecl | expressions.swift:151:18:151:53 | keyPathInt | ParamDecl | | expressions.swift:151:1:155:1 | test(a:keyPathInt:keyPathB:) | ConcreteFuncDecl | expressions.swift:151:56:151:87 | keyPathB | ParamDecl | @@ -814,9 +812,7 @@ | expressions.swift:154:22:154:41 | \\...[...] | KeyPathApplicationExpr | expressions.swift:154:33:154:33 | keyPathB | DeclRefExpr | | expressions.swift:154:22:154:56 | \\...[...] | KeyPathApplicationExpr | expressions.swift:154:22:154:41 | \\...[...] | KeyPathApplicationExpr | | expressions.swift:154:22:154:56 | \\...[...] | KeyPathApplicationExpr | expressions.swift:154:52:154:55 | #keyPath(...) | KeyPathExpr | -| expressions.swift:154:52:154:55 | #keyPath(...) | KeyPathExpr | expressions.swift:154:53:154:55 | ... .x | UnresolvedDotExpr | -| expressions.swift:154:53:154:53 | (no string representation) | TypeExpr | expressions.swift:154:53:154:53 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | -| expressions.swift:154:53:154:55 | ... .x | UnresolvedDotExpr | expressions.swift:154:53:154:53 | (no string representation) | TypeExpr | +| expressions.swift:154:52:154:55 | #keyPath(...) | KeyPathExpr | expressions.swift:154:53:154:53 | B | TypeRepr | | patterns.swift:1:1:7:1 | basic_patterns() | ConcreteFuncDecl | patterns.swift:1:23:7:1 | { ... } | BraceStmt | | patterns.swift:1:23:7:1 | { ... } | BraceStmt | patterns.swift:2:5:2:18 | var ... = ... | PatternBindingDecl | | patterns.swift:1:23:7:1 | { ... } | BraceStmt | patterns.swift:2:9:2:9 | an_int | ConcreteVarDecl | @@ -833,7 +829,7 @@ | patterns.swift:3:5:3:28 | var ... = ... | PatternBindingDecl | patterns.swift:3:9:3:19 | ... as ... | TypedPattern | | patterns.swift:3:5:3:28 | var ... = ... | PatternBindingDecl | patterns.swift:3:28:3:28 | here | StringLiteralExpr | | patterns.swift:3:9:3:19 | ... as ... | TypedPattern | patterns.swift:3:9:3:9 | a_string | NamedPattern | -| patterns.swift:3:9:3:19 | ... as ... | TypedPattern | patterns.swift:3:19:3:19 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| patterns.swift:3:9:3:19 | ... as ... | TypedPattern | patterns.swift:3:19:3:19 | String | TypeRepr | | patterns.swift:4:5:4:29 | var ... = ... | PatternBindingDecl | patterns.swift:4:9:4:17 | (...) | TuplePattern | | patterns.swift:4:5:4:29 | var ... = ... | PatternBindingDecl | patterns.swift:4:21:4:29 | (...) | TupleExpr | | patterns.swift:4:9:4:17 | (...) | TuplePattern | patterns.swift:4:10:4:10 | x | NamedPattern | @@ -900,8 +896,8 @@ | patterns.swift:24:5:24:19 | var ... = ... | PatternBindingDecl | patterns.swift:24:9:24:12 | ... as ... | TypedPattern | | patterns.swift:24:5:24:19 | var ... = ... | PatternBindingDecl | patterns.swift:24:18:24:19 | call to ... | DotSyntaxCallExpr | | patterns.swift:24:9:24:12 | ... as ... | TypedPattern | patterns.swift:24:9:24:9 | v | NamedPattern | -| patterns.swift:24:9:24:12 | ... as ... | TypedPattern | patterns.swift:24:12:24:12 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | -| patterns.swift:24:18:24:18 | Foo.Type | TypeExpr | patterns.swift:24:18:24:18 | FixedTypeRepr | FixedTypeRepr | +| patterns.swift:24:9:24:12 | ... as ... | TypedPattern | patterns.swift:24:12:24:12 | Foo | TypeRepr | +| patterns.swift:24:18:24:18 | Foo.Type | TypeExpr | patterns.swift:24:18:24:18 | Foo | TypeRepr | | patterns.swift:24:18:24:19 | call to ... | DotSyntaxCallExpr | patterns.swift:24:19:24:19 | bar | DeclRefExpr | | patterns.swift:26:5:29:5 | switch v { ... } | SwitchStmt | patterns.swift:26:12:26:12 | v | DeclRefExpr | | patterns.swift:26:5:29:5 | switch v { ... } | SwitchStmt | patterns.swift:27:5:27:16 | case ... | CaseStmt | @@ -921,7 +917,7 @@ | patterns.swift:31:5:31:19 | var ... = ... | PatternBindingDecl | patterns.swift:31:9:31:15 | ... as ... | TypedPattern | | patterns.swift:31:5:31:19 | var ... = ... | PatternBindingDecl | patterns.swift:31:19:31:19 | nil | NilLiteralExpr | | patterns.swift:31:9:31:15 | ... as ... | TypedPattern | patterns.swift:31:9:31:9 | w | NamedPattern | -| patterns.swift:31:9:31:15 | ... as ... | TypedPattern | patterns.swift:31:12:31:15 | ...? | OptionalTypeRepr | +| patterns.swift:31:9:31:15 | ... as ... | TypedPattern | patterns.swift:31:12:31:15 | Int? | TypeRepr | | patterns.swift:33:5:36:5 | switch w { ... } | SwitchStmt | patterns.swift:33:12:33:12 | w | DeclRefExpr | | patterns.swift:33:5:36:5 | switch w { ... } | SwitchStmt | patterns.swift:34:5:34:18 | case ... | CaseStmt | | patterns.swift:33:5:36:5 | switch w { ... } | SwitchStmt | patterns.swift:35:5:35:13 | case ... | CaseStmt | @@ -938,7 +934,7 @@ | patterns.swift:38:5:38:18 | var ... = ... | PatternBindingDecl | patterns.swift:38:9:38:12 | ... as ... | TypedPattern | | patterns.swift:38:5:38:18 | var ... = ... | PatternBindingDecl | patterns.swift:38:18:38:18 | (Any) ... | ErasureExpr | | patterns.swift:38:9:38:12 | ... as ... | TypedPattern | patterns.swift:38:9:38:9 | a | NamedPattern | -| patterns.swift:38:9:38:12 | ... as ... | TypedPattern | patterns.swift:38:12:38:12 | CompositionTypeRepr | CompositionTypeRepr | +| patterns.swift:38:9:38:12 | ... as ... | TypedPattern | patterns.swift:38:12:38:12 | Any | TypeRepr | | patterns.swift:38:18:38:18 | (Any) ... | ErasureExpr | patterns.swift:38:18:38:18 | any | StringLiteralExpr | | patterns.swift:40:5:44:5 | switch a { ... } | SwitchStmt | patterns.swift:40:12:40:12 | a | DeclRefExpr | | patterns.swift:40:5:44:5 | switch a { ... } | SwitchStmt | patterns.swift:41:5:41:18 | case ... | CaseStmt | @@ -947,14 +943,14 @@ | patterns.swift:41:5:41:18 | case ... | CaseStmt | patterns.swift:41:10:41:13 | ... is ... | CaseLabelItem | | patterns.swift:41:5:41:18 | case ... | CaseStmt | patterns.swift:41:18:41:18 | { ... } | BraceStmt | | patterns.swift:41:10:41:13 | ... is ... | CaseLabelItem | patterns.swift:41:10:41:13 | ... is ... | IsPattern | -| patterns.swift:41:10:41:13 | ... is ... | IsPattern | patterns.swift:41:13:41:13 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| patterns.swift:41:10:41:13 | ... is ... | IsPattern | patterns.swift:41:13:41:13 | Int | TypeRepr | | patterns.swift:41:18:41:18 | { ... } | BraceStmt | patterns.swift:41:18:41:18 | is pattern | StringLiteralExpr | | patterns.swift:42:5:42:27 | case ... | CaseStmt | patterns.swift:42:10:42:19 | let ... | CaseLabelItem | | patterns.swift:42:5:42:27 | case ... | CaseStmt | patterns.swift:42:27:42:27 | { ... } | BraceStmt | | patterns.swift:42:10:42:19 | let ... | BindingPattern | patterns.swift:42:14:42:19 | ... is ... | IsPattern | | patterns.swift:42:10:42:19 | let ... | CaseLabelItem | patterns.swift:42:10:42:19 | let ... | BindingPattern | | patterns.swift:42:14:42:19 | ... is ... | IsPattern | patterns.swift:42:14:42:14 | x | NamedPattern | -| patterns.swift:42:14:42:19 | ... is ... | IsPattern | patterns.swift:42:19:42:19 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| patterns.swift:42:14:42:19 | ... is ... | IsPattern | patterns.swift:42:19:42:19 | String | TypeRepr | | patterns.swift:42:27:42:27 | { ... } | BraceStmt | patterns.swift:42:27:42:27 | as pattern | StringLiteralExpr | | patterns.swift:43:5:43:13 | case ... | CaseStmt | patterns.swift:43:10:43:10 | _ | CaseLabelItem | | patterns.swift:43:5:43:13 | case ... | CaseStmt | patterns.swift:43:13:43:13 | { ... } | BraceStmt | @@ -986,14 +982,14 @@ | statements.swift:2:3:8:3 | for ... in ... { ... } | ForEachStmt | statements.swift:2:20:2:24 | ... call to ...(_:_:) ... | BinaryExpr | | statements.swift:2:3:8:3 | for ... in ... { ... } | ForEachStmt | statements.swift:2:26:8:3 | { ... } | BraceStmt | | statements.swift:2:20:2:24 | ... call to ...(_:_:) ... | BinaryExpr | statements.swift:2:21:2:21 | call to ...(_:_:) | DotSyntaxCallExpr | -| statements.swift:2:21:2:21 | Int.Type | TypeExpr | statements.swift:2:21:2:21 | FixedTypeRepr | FixedTypeRepr | +| statements.swift:2:21:2:21 | Int.Type | TypeExpr | statements.swift:2:21:2:21 | Int | TypeRepr | | statements.swift:2:21:2:21 | call to ...(_:_:) | DotSyntaxCallExpr | statements.swift:2:21:2:21 | ...(_:_:) | DeclRefExpr | | statements.swift:2:26:8:3 | { ... } | BraceStmt | statements.swift:3:5:7:5 | if ... then { ... } else { ... } | IfStmt | | statements.swift:3:5:7:5 | if ... then { ... } else { ... } | IfStmt | statements.swift:3:8:3:13 | StmtCondition | StmtCondition | | statements.swift:3:5:7:5 | if ... then { ... } else { ... } | IfStmt | statements.swift:3:15:5:5 | { ... } | BraceStmt | | statements.swift:3:5:7:5 | if ... then { ... } else { ... } | IfStmt | statements.swift:5:12:7:5 | { ... } | BraceStmt | | statements.swift:3:8:3:13 | ... call to ==(_:_:) ... | BinaryExpr | statements.swift:3:10:3:10 | call to ==(_:_:) | DotSyntaxCallExpr | -| statements.swift:3:10:3:10 | Int.Type | TypeExpr | statements.swift:3:10:3:10 | FixedTypeRepr | FixedTypeRepr | +| statements.swift:3:10:3:10 | Int.Type | TypeExpr | statements.swift:3:10:3:10 | Int | TypeRepr | | statements.swift:3:10:3:10 | call to ==(_:_:) | DotSyntaxCallExpr | statements.swift:3:10:3:10 | ==(_:_:) | DeclRefExpr | | statements.swift:3:15:5:5 | { ... } | BraceStmt | statements.swift:4:9:4:9 | break | BreakStmt | | statements.swift:5:12:7:5 | { ... } | BraceStmt | statements.swift:6:9:6:9 | continue | ContinueStmt | @@ -1004,14 +1000,14 @@ | statements.swift:10:17:10:24 | (...) | ParenExpr | statements.swift:10:18:10:22 | ... call to <(_:_:) ... | BinaryExpr | | statements.swift:10:18:10:18 | (Int) ... | LoadExpr | statements.swift:10:18:10:18 | i | DeclRefExpr | | statements.swift:10:18:10:22 | ... call to <(_:_:) ... | BinaryExpr | statements.swift:10:20:10:20 | call to <(_:_:) | DotSyntaxCallExpr | -| statements.swift:10:20:10:20 | Int.Type | TypeExpr | statements.swift:10:20:10:20 | FixedTypeRepr | FixedTypeRepr | +| statements.swift:10:20:10:20 | Int.Type | TypeExpr | statements.swift:10:20:10:20 | Int | TypeRepr | | statements.swift:10:20:10:20 | call to <(_:_:) | DotSyntaxCallExpr | statements.swift:10:20:10:20 | <(_:_:) | DeclRefExpr | | statements.swift:10:26:12:3 | { ... } | BraceStmt | statements.swift:11:5:11:13 | ... = ... | AssignExpr | | statements.swift:11:5:11:13 | ... = ... | AssignExpr | statements.swift:11:5:11:5 | i | DeclRefExpr | | statements.swift:11:5:11:13 | ... = ... | AssignExpr | statements.swift:11:9:11:13 | ... call to +(_:_:) ... | BinaryExpr | | statements.swift:11:9:11:9 | (Int) ... | LoadExpr | statements.swift:11:9:11:9 | i | DeclRefExpr | | statements.swift:11:9:11:13 | ... call to +(_:_:) ... | BinaryExpr | statements.swift:11:11:11:11 | call to +(_:_:) | DotSyntaxCallExpr | -| statements.swift:11:11:11:11 | Int.Type | TypeExpr | statements.swift:11:11:11:11 | FixedTypeRepr | FixedTypeRepr | +| statements.swift:11:11:11:11 | Int.Type | TypeExpr | statements.swift:11:11:11:11 | Int | TypeRepr | | statements.swift:11:11:11:11 | call to +(_:_:) | DotSyntaxCallExpr | statements.swift:11:11:11:11 | +(_:_:) | DeclRefExpr | | statements.swift:14:3:14:7 | ... = ... | AssignExpr | statements.swift:14:3:14:3 | i | DeclRefExpr | | statements.swift:14:3:14:7 | ... = ... | AssignExpr | statements.swift:14:7:14:7 | 0 | IntegerLiteralExpr | @@ -1022,12 +1018,12 @@ | statements.swift:16:5:16:13 | ... = ... | AssignExpr | statements.swift:16:9:16:13 | ... call to +(_:_:) ... | BinaryExpr | | statements.swift:16:9:16:9 | (Int) ... | LoadExpr | statements.swift:16:9:16:9 | i | DeclRefExpr | | statements.swift:16:9:16:13 | ... call to +(_:_:) ... | BinaryExpr | statements.swift:16:11:16:11 | call to +(_:_:) | DotSyntaxCallExpr | -| statements.swift:16:11:16:11 | Int.Type | TypeExpr | statements.swift:16:11:16:11 | FixedTypeRepr | FixedTypeRepr | +| statements.swift:16:11:16:11 | Int.Type | TypeExpr | statements.swift:16:11:16:11 | Int | TypeRepr | | statements.swift:16:11:16:11 | call to +(_:_:) | DotSyntaxCallExpr | statements.swift:16:11:16:11 | +(_:_:) | DeclRefExpr | | statements.swift:17:11:17:18 | (...) | ParenExpr | statements.swift:17:12:17:16 | ... call to <(_:_:) ... | BinaryExpr | | statements.swift:17:12:17:12 | (Int) ... | LoadExpr | statements.swift:17:12:17:12 | i | DeclRefExpr | | statements.swift:17:12:17:16 | ... call to <(_:_:) ... | BinaryExpr | statements.swift:17:14:17:14 | call to <(_:_:) | DotSyntaxCallExpr | -| statements.swift:17:14:17:14 | Int.Type | TypeExpr | statements.swift:17:14:17:14 | FixedTypeRepr | FixedTypeRepr | +| statements.swift:17:14:17:14 | Int.Type | TypeExpr | statements.swift:17:14:17:14 | Int | TypeRepr | | statements.swift:17:14:17:14 | call to <(_:_:) | DotSyntaxCallExpr | statements.swift:17:14:17:14 | <(_:_:) | DeclRefExpr | | statements.swift:19:3:23:3 | do { ... } catch { ... } | DoCatchStmt | statements.swift:19:6:21:3 | { ... } | BraceStmt | | statements.swift:19:3:23:3 | do { ... } catch { ... } | DoCatchStmt | statements.swift:21:5:23:3 | case ... | CaseStmt | @@ -1074,11 +1070,11 @@ | statements.swift:39:3:41:3 | guard ... else { ... } | GuardStmt | statements.swift:39:9:39:14 | StmtCondition | StmtCondition | | statements.swift:39:3:41:3 | guard ... else { ... } | GuardStmt | statements.swift:39:21:41:3 | { ... } | BraceStmt | | statements.swift:39:9:39:14 | ... call to !=(_:_:) ... | BinaryExpr | statements.swift:39:11:39:11 | call to !=(_:_:) | DotSyntaxCallExpr | -| statements.swift:39:11:39:11 | Int.Type | TypeExpr | statements.swift:39:11:39:11 | FixedTypeRepr | FixedTypeRepr | +| statements.swift:39:11:39:11 | Int.Type | TypeExpr | statements.swift:39:11:39:11 | Int | TypeRepr | | statements.swift:39:11:39:11 | call to !=(_:_:) | DotSyntaxCallExpr | statements.swift:39:11:39:11 | !=(_:_:) | DeclRefExpr | | statements.swift:39:21:41:3 | { ... } | BraceStmt | statements.swift:40:5:40:19 | throw ... | ThrowStmt | | statements.swift:40:5:40:19 | throw ... | ThrowStmt | statements.swift:40:11:40:19 | (Error) ... | ErasureExpr | -| statements.swift:40:11:40:11 | AnError.Type | TypeExpr | statements.swift:40:11:40:11 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| statements.swift:40:11:40:11 | AnError.Type | TypeExpr | statements.swift:40:11:40:11 | AnError | TypeRepr | | statements.swift:40:11:40:19 | (Error) ... | ErasureExpr | statements.swift:40:11:40:19 | call to ... | DotSyntaxCallExpr | | statements.swift:40:11:40:19 | call to ... | DotSyntaxCallExpr | statements.swift:40:19:40:19 | failed | DeclRefExpr | | statements.swift:44:1:46:1 | defer { ... } | DeferStmt | statements.swift:44:7:46:1 | { ... } | BraceStmt | @@ -1143,7 +1139,7 @@ | statements.swift:64:1:64:15 | { ... } | BraceStmt | statements.swift:64:1:64:15 | var ... = ... | PatternBindingDecl | | statements.swift:64:1:64:15 | { ... } | TopLevelCodeDecl | statements.swift:64:1:64:15 | { ... } | BraceStmt | | statements.swift:64:5:64:11 | ... as ... | TypedPattern | statements.swift:64:5:64:5 | x | NamedPattern | -| statements.swift:64:5:64:11 | ... as ... | TypedPattern | statements.swift:64:8:64:11 | ...? | OptionalTypeRepr | +| statements.swift:64:5:64:11 | ... as ... | TypedPattern | statements.swift:64:8:64:11 | Int? | TypeRepr | | statements.swift:64:15:64:15 | (Int?) ... | InjectIntoOptionalExpr | statements.swift:64:15:64:15 | 4 | IntegerLiteralExpr | | statements.swift:65:1:66:1 | if ... then { ... } | IfStmt | statements.swift:65:4:65:19 | StmtCondition | StmtCondition | | statements.swift:65:1:66:1 | if ... then { ... } | IfStmt | statements.swift:65:21:66:1 | { ... } | BraceStmt | @@ -1172,9 +1168,9 @@ | statements.swift:71:1:72:1 | { ... } | TopLevelCodeDecl | statements.swift:71:1:72:1 | { ... } | BraceStmt | | statements.swift:71:29:71:38 | ... call to %(_:_:) ... | BinaryExpr | statements.swift:71:36:71:36 | call to %(_:_:) | DotSyntaxCallExpr | | statements.swift:71:29:71:43 | ... call to ==(_:_:) ... | BinaryExpr | statements.swift:71:40:71:40 | call to ==(_:_:) | DotSyntaxCallExpr | -| statements.swift:71:36:71:36 | Int.Type | TypeExpr | statements.swift:71:36:71:36 | FixedTypeRepr | FixedTypeRepr | +| statements.swift:71:36:71:36 | Int.Type | TypeExpr | statements.swift:71:36:71:36 | Int | TypeRepr | | statements.swift:71:36:71:36 | call to %(_:_:) | DotSyntaxCallExpr | statements.swift:71:36:71:36 | %(_:_:) | DeclRefExpr | -| statements.swift:71:40:71:40 | Int.Type | TypeExpr | statements.swift:71:40:71:40 | FixedTypeRepr | FixedTypeRepr | +| statements.swift:71:40:71:40 | Int.Type | TypeExpr | statements.swift:71:40:71:40 | Int | TypeRepr | | statements.swift:71:40:71:40 | call to ==(_:_:) | DotSyntaxCallExpr | statements.swift:71:40:71:40 | ==(_:_:) | DeclRefExpr | | statements.swift:74:8:74:8 | init | ConstructorDecl | statements.swift:74:8:74:8 | x | ParamDecl | | statements.swift:75:3:75:11 | var ... = ... | PatternBindingDecl | statements.swift:75:7:75:11 | ... as ... | TypedPattern | @@ -1190,7 +1186,7 @@ | statements.swift:75:7:75:7 | { ... } | BraceStmt | file://:0:0:0:0 | return ... | ReturnStmt | | statements.swift:75:7:75:7 | { ... } | BraceStmt | statements.swift:75:7:75:7 | yield ... | YieldStmt | | statements.swift:75:7:75:11 | ... as ... | TypedPattern | statements.swift:75:7:75:7 | x | NamedPattern | -| statements.swift:75:7:75:11 | ... as ... | TypedPattern | statements.swift:75:11:75:11 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| statements.swift:75:7:75:11 | ... as ... | TypedPattern | statements.swift:75:11:75:11 | Int | TypeRepr | | statements.swift:76:3:84:3 | var ... = ... | PatternBindingDecl | statements.swift:76:7:76:19 | ... as ... | TypedPattern | | statements.swift:76:7:76:7 | hasModify | ConcreteVarDecl | statements.swift:76:7:76:7 | set | AccessorDecl | | statements.swift:76:7:76:7 | hasModify | ConcreteVarDecl | statements.swift:77:5:79:5 | (unnamed function decl) | AccessorDecl | @@ -1199,7 +1195,7 @@ | statements.swift:76:7:76:7 | set | AccessorDecl | statements.swift:76:7:76:7 | { ... } | BraceStmt | | statements.swift:76:7:76:7 | { ... } | BraceStmt | file://:0:0:0:0 | ... = ... | AssignExpr | | statements.swift:76:7:76:19 | ... as ... | TypedPattern | statements.swift:76:7:76:7 | hasModify | NamedPattern | -| statements.swift:76:7:76:19 | ... as ... | TypedPattern | statements.swift:76:19:76:19 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| statements.swift:76:7:76:19 | ... as ... | TypedPattern | statements.swift:76:19:76:19 | Int | TypeRepr | | statements.swift:77:5:79:5 | (unnamed function decl) | AccessorDecl | statements.swift:77:13:79:5 | { ... } | BraceStmt | | statements.swift:77:13:79:5 | { ... } | BraceStmt | statements.swift:78:7:78:14 | yield ... | YieldStmt | | statements.swift:78:7:78:14 | yield ... | YieldStmt | statements.swift:78:13:78:14 | &... | InOutExpr | From 965f5a980af40bc7101a3d093818ca70087a7ebe Mon Sep 17 00:00:00 2001 From: Ian Lynagh Date: Tue, 12 Jul 2022 10:58:16 +0100 Subject: [PATCH 187/505] Java/Kotlin: Add changenote for ErrorType --- java/ql/lib/change-notes/2022-07-12-errortype.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 java/ql/lib/change-notes/2022-07-12-errortype.md diff --git a/java/ql/lib/change-notes/2022-07-12-errortype.md b/java/ql/lib/change-notes/2022-07-12-errortype.md new file mode 100644 index 00000000000..7f7e1231ddc --- /dev/null +++ b/java/ql/lib/change-notes/2022-07-12-errortype.md @@ -0,0 +1,4 @@ +--- +category: feature +--- +* Added an `ErrorType` class. An instance of this class will be used if an extractor is unable to extract as type, or if an up/downgrade script is unable to provide a type. From 48c71c94076b8953930de958978fcd9f1b92905c Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Tue, 12 Jul 2022 12:10:22 +0200 Subject: [PATCH 188/505] Swift: add comment about `TypeRepr` in `ASTNode` fetching --- swift/extractor/infra/SwiftDispatcher.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/swift/extractor/infra/SwiftDispatcher.h b/swift/extractor/infra/SwiftDispatcher.h index 0f1753cda1d..42b0faa5a5a 100644 --- a/swift/extractor/infra/SwiftDispatcher.h +++ b/swift/extractor/infra/SwiftDispatcher.h @@ -251,6 +251,10 @@ class SwiftDispatcher { template bool fetchLabelFromUnionCase(const llvm::PointerUnion u, TrapLabel& output) { + // we rely on the fact that when we extract `ASTNode` instances (which only happens + // on `BraceStmt` elements), we cannot encounter a standalone `TypeRepr` there, so we skip + // this case, which would be problematic as we would not be able to provide the corresponding + // type if constexpr (!std::is_same_v) { if (auto e = u.template dyn_cast()) { output = fetchLabel(e); From 1bcb17b760c3be7cd6508800baafa427545c81b9 Mon Sep 17 00:00:00 2001 From: Ian Lynagh Date: Tue, 12 Jul 2022 12:16:24 +0100 Subject: [PATCH 189/505] Update java/ql/lib/change-notes/2022-07-12-errortype.md Co-authored-by: Anders Schack-Mulligen --- java/ql/lib/change-notes/2022-07-12-errortype.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/lib/change-notes/2022-07-12-errortype.md b/java/ql/lib/change-notes/2022-07-12-errortype.md index 7f7e1231ddc..97f851bb936 100644 --- a/java/ql/lib/change-notes/2022-07-12-errortype.md +++ b/java/ql/lib/change-notes/2022-07-12-errortype.md @@ -1,4 +1,4 @@ --- category: feature --- -* Added an `ErrorType` class. An instance of this class will be used if an extractor is unable to extract as type, or if an up/downgrade script is unable to provide a type. +* Added an `ErrorType` class. An instance of this class will be used if an extractor is unable to extract a type, or if an up/downgrade script is unable to provide a type. From 2ceb25dc9a7d40f20170afc15b25ba639daa6e41 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Tue, 12 Jul 2022 15:21:37 +0200 Subject: [PATCH 190/505] C++: Order left and right operands in the logical left to right order --- cpp/ql/lib/semmle/code/cpp/controlflow/Nullness.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/controlflow/Nullness.qll b/cpp/ql/lib/semmle/code/cpp/controlflow/Nullness.qll index 92beb5c9d99..baa78f7be7c 100644 --- a/cpp/ql/lib/semmle/code/cpp/controlflow/Nullness.qll +++ b/cpp/ql/lib/semmle/code/cpp/controlflow/Nullness.qll @@ -46,7 +46,7 @@ predicate nullCheckExpr(Expr checkExpr, Variable var) { or exists(LogicalAndExpr op, AnalysedExpr child | expr = op and - (op.getRightOperand() = child or op.getLeftOperand() = child) and + (op.getLeftOperand() = child or op.getRightOperand() = child) and nullCheckExpr(child, v) ) or @@ -99,7 +99,7 @@ predicate validCheckExpr(Expr checkExpr, Variable var) { or exists(LogicalAndExpr op, AnalysedExpr child | expr = op and - (op.getRightOperand() = child or op.getLeftOperand() = child) and + (op.getLeftOperand() = child or op.getRightOperand() = child) and validCheckExpr(child, v) ) or From d63b0946d98eb65edc87f31ce2005f73cdcbdb3d Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Tue, 12 Jul 2022 15:22:13 +0200 Subject: [PATCH 191/505] C++: Use `ConditionDeclExpr` in `AnalysedExpr::isDef` --- cpp/ql/lib/semmle/code/cpp/controlflow/Nullness.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/controlflow/Nullness.qll b/cpp/ql/lib/semmle/code/cpp/controlflow/Nullness.qll index baa78f7be7c..a64c6a277d4 100644 --- a/cpp/ql/lib/semmle/code/cpp/controlflow/Nullness.qll +++ b/cpp/ql/lib/semmle/code/cpp/controlflow/Nullness.qll @@ -171,8 +171,8 @@ class AnalysedExpr extends Expr { this.inCondition() and ( this.(Assignment).getLValue() = v.getAnAccess() or - exists(Initializer i | this.getEnclosingStmt() = i.getEnclosingStmt() and v = i.getDeclaration()) - ) + this.(ConditionDeclExpr).getVariableAccess() = v.getAnAccess() + ) } /** From e5eabc4e4730f7ed7fd0f2735baf3e64cc998f10 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Tue, 12 Jul 2022 15:23:33 +0200 Subject: [PATCH 192/505] C++: Slightly tweak nullness test and update test results --- .../controlflow/nullness/nullness.expected | 11 ++++++++--- .../library-tests/controlflow/nullness/nullness.ql | 1 - .../test/library-tests/controlflow/nullness/test.cpp | 6 ++++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/cpp/ql/test/library-tests/controlflow/nullness/nullness.expected b/cpp/ql/test/library-tests/controlflow/nullness/nullness.expected index db5c795fd5b..bcf301ba47b 100644 --- a/cpp/ql/test/library-tests/controlflow/nullness/nullness.expected +++ b/cpp/ql/test/library-tests/controlflow/nullness/nullness.expected @@ -7,9 +7,14 @@ | test.cpp:15:8:15:23 | call to __builtin_expect | test.cpp:5:13:5:13 | v | is not null | is valid | | test.cpp:16:8:16:23 | call to __builtin_expect | test.cpp:5:13:5:13 | v | is null | is not valid | | test.cpp:17:9:17:17 | ... && ... | test.cpp:5:13:5:13 | v | is not null | is valid | -| test.cpp:18:9:18:17 | ... && ... | test.cpp:5:13:5:13 | v | is not null | is not valid | +| test.cpp:18:9:18:17 | ... && ... | test.cpp:5:13:5:13 | v | is not null | is valid | | test.cpp:19:9:19:18 | ... && ... | test.cpp:5:13:5:13 | v | is null | is not valid | -| test.cpp:20:9:20:18 | ... && ... | test.cpp:5:13:5:13 | v | is not null | is not valid | +| test.cpp:20:9:20:18 | ... && ... | test.cpp:5:13:5:13 | v | is null | is not valid | | test.cpp:21:9:21:14 | ... = ... | test.cpp:5:13:5:13 | v | is null | is not valid | | test.cpp:21:9:21:14 | ... = ... | test.cpp:7:10:7:10 | b | is not null | is valid | -| test.cpp:22:17:22:17 | b | test.cpp:7:10:7:10 | b | is not null | is valid | +| test.cpp:22:9:22:14 | ... = ... | test.cpp:5:13:5:13 | v | is not null | is not valid | +| test.cpp:22:9:22:14 | ... = ... | test.cpp:7:13:7:13 | c | is not null | is not valid | +| test.cpp:22:17:22:17 | c | test.cpp:7:13:7:13 | c | is not null | is valid | +| test.cpp:23:21:23:21 | x | test.cpp:23:14:23:14 | x | is not null | is valid | +| test.cpp:24:9:24:18 | (condition decl) | test.cpp:5:13:5:13 | v | is not null | is not valid | +| test.cpp:24:9:24:18 | (condition decl) | test.cpp:24:14:24:14 | y | is not null | is valid | diff --git a/cpp/ql/test/library-tests/controlflow/nullness/nullness.ql b/cpp/ql/test/library-tests/controlflow/nullness/nullness.ql index ed1ba15aa2b..864fd04f920 100644 --- a/cpp/ql/test/library-tests/controlflow/nullness/nullness.ql +++ b/cpp/ql/test/library-tests/controlflow/nullness/nullness.ql @@ -2,7 +2,6 @@ import cpp from AnalysedExpr a, LocalScopeVariable v, string isNullCheck, string isValidCheck where - a.getParent() instanceof IfStmt and v.getAnAccess().getEnclosingStmt() = a.getParent() and (if a.isNullCheck(v) then isNullCheck = "is null" else isNullCheck = "is not null") and (if a.isValidCheck(v) then isValidCheck = "is valid" else isValidCheck = "is not valid") diff --git a/cpp/ql/test/library-tests/controlflow/nullness/test.cpp b/cpp/ql/test/library-tests/controlflow/nullness/test.cpp index 03369c811d5..407753be17a 100644 --- a/cpp/ql/test/library-tests/controlflow/nullness/test.cpp +++ b/cpp/ql/test/library-tests/controlflow/nullness/test.cpp @@ -4,7 +4,7 @@ long __builtin_expect(long); void f(int *v) { int *w; - bool b; + bool b, c; if (v) {} if (!v) {} @@ -19,5 +19,7 @@ void f(int *v) { if (true && !v) {} if (!v && true) {} if (b = !v) {} - if (b = !v; b) {} + if (c = !v; c) {} + if (int *x = v; x) {} + if (int *y = v) {} } From 8f9d4194413f5c836ff53f6312e0a9ecfc8251c7 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Tue, 12 Jul 2022 15:24:09 +0200 Subject: [PATCH 193/505] C++: Add change note --- .../lib/change-notes/2022-07-12-cover-more-nullness-cases.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 cpp/ql/lib/change-notes/2022-07-12-cover-more-nullness-cases.md diff --git a/cpp/ql/lib/change-notes/2022-07-12-cover-more-nullness-cases.md b/cpp/ql/lib/change-notes/2022-07-12-cover-more-nullness-cases.md new file mode 100644 index 00000000000..eef564991f5 --- /dev/null +++ b/cpp/ql/lib/change-notes/2022-07-12-cover-more-nullness-cases.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* `AnalysedExpr::isNullCheck` and `AnalysedExpr::isValidCheck` have been updated to handle variable accesses on the left-hand side of the the C++ logical and variable declarations in conditions. From f7c4fa691d7918c86be65f20ae1778bc2ff0097b Mon Sep 17 00:00:00 2001 From: Jeroen Ketema <93738568+jketema@users.noreply.github.com> Date: Tue, 12 Jul 2022 16:59:15 +0200 Subject: [PATCH 194/505] Apply suggestions from code review Co-authored-by: Geoffrey White <40627776+geoffw0@users.noreply.github.com> --- cpp/ql/lib/semmle/code/cpp/controlflow/Nullness.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/controlflow/Nullness.qll b/cpp/ql/lib/semmle/code/cpp/controlflow/Nullness.qll index a64c6a277d4..0fb46f75c94 100644 --- a/cpp/ql/lib/semmle/code/cpp/controlflow/Nullness.qll +++ b/cpp/ql/lib/semmle/code/cpp/controlflow/Nullness.qll @@ -46,7 +46,7 @@ predicate nullCheckExpr(Expr checkExpr, Variable var) { or exists(LogicalAndExpr op, AnalysedExpr child | expr = op and - (op.getLeftOperand() = child or op.getRightOperand() = child) and + op.getAnOperand() = child and nullCheckExpr(child, v) ) or @@ -99,7 +99,7 @@ predicate validCheckExpr(Expr checkExpr, Variable var) { or exists(LogicalAndExpr op, AnalysedExpr child | expr = op and - (op.getLeftOperand() = child or op.getRightOperand() = child) and + op.getAnOperand() = child and validCheckExpr(child, v) ) or From a51d713925cd351664efa9e2905f1b63b2b8b32f Mon Sep 17 00:00:00 2001 From: Raul Garcia <42392023+raulgarciamsft@users.noreply.github.com> Date: Tue, 12 Jul 2022 08:13:12 -0700 Subject: [PATCH 195/505] Update java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql Co-authored-by: Chris Smowton --- .../CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql b/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql index 50840b67f0b..d3b742f7c95 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql @@ -14,8 +14,8 @@ import java import semmle.code.java.dataflow.DataFlow /** - * Holds if the call `call` is an object creation for a class `EncryptedBlobClientBuilder` - * that takes no arguments, which means that it is using V1 encryption + * Holds if `call` is an object creation for a class `EncryptedBlobClientBuilder` + * that takes no arguments, which means that it is using V1 encryption. */ predicate isCreatingOutdatedAzureClientSideEncryptionObject(Call call, Class c) { exists(string package, string type, Constructor constructor | From a4e35a97eac187d0b6a3a6cb2f72c9f0d8340bb6 Mon Sep 17 00:00:00 2001 From: Raul Garcia <42392023+raulgarciamsft@users.noreply.github.com> Date: Tue, 12 Jul 2022 08:13:38 -0700 Subject: [PATCH 196/505] Update java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql Co-authored-by: Chris Smowton --- .../CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql b/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql index d3b742f7c95..c0fd959b790 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql @@ -33,8 +33,8 @@ predicate isCreatingOutdatedAzureClientSideEncryptionObject(Call call, Class c) } /** - * Holds if the call `call` is an object creation for a class `EncryptedBlobClientBuilder` - * that takes `versionArg` as the argument for the version. + * Holds if `call` is an object creation for a class `EncryptedBlobClientBuilder` + * that takes `versionArg` as the argument specifying the encryption version. */ predicate isCreatingAzureClientSideEncryptionObjectNewVersion(Call call, Class c, Expr versionArg) { exists(string package, string type, Constructor constructor | From 2bac18109487a757e72f997bab20c2fd01ba6b50 Mon Sep 17 00:00:00 2001 From: Raul Garcia <42392023+raulgarciamsft@users.noreply.github.com> Date: Tue, 12 Jul 2022 08:13:53 -0700 Subject: [PATCH 197/505] Update java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql Co-authored-by: Chris Smowton --- .../CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql b/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql index c0fd959b790..5f73e4322b0 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql @@ -48,7 +48,7 @@ predicate isCreatingAzureClientSideEncryptionObjectNewVersion(Call call, Class c } /** - * A config that tracks `EncryptedBlobClientBuilder.version` argument initialization. + * A dataflow config that tracks `EncryptedBlobClientBuilder.version` argument initialization. */ private class EncryptedBlobClientBuilderEncryptionVersionConfig extends DataFlow::Configuration { EncryptedBlobClientBuilderEncryptionVersionConfig() { From 8a48708014bfcd4afb9dd6c3d8d0123bc707ae97 Mon Sep 17 00:00:00 2001 From: Raul Garcia <42392023+raulgarciamsft@users.noreply.github.com> Date: Tue, 12 Jul 2022 08:14:13 -0700 Subject: [PATCH 198/505] Update java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql Co-authored-by: Chris Smowton --- .../CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql b/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql index 5f73e4322b0..b5ac2cabd7e 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql @@ -50,7 +50,7 @@ predicate isCreatingAzureClientSideEncryptionObjectNewVersion(Call call, Class c /** * A dataflow config that tracks `EncryptedBlobClientBuilder.version` argument initialization. */ -private class EncryptedBlobClientBuilderEncryptionVersionConfig extends DataFlow::Configuration { +private class EncryptedBlobClientBuilderSafeEncryptionVersionConfig extends DataFlow::Configuration { EncryptedBlobClientBuilderEncryptionVersionConfig() { this = "EncryptedBlobClientBuilderEncryptionVersionConfig" } From 64343e00f4c6fe61b9f6ed263c5f3a3c843605f7 Mon Sep 17 00:00:00 2001 From: Raul Garcia <42392023+raulgarciamsft@users.noreply.github.com> Date: Tue, 12 Jul 2022 08:14:25 -0700 Subject: [PATCH 199/505] Update java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql Co-authored-by: Chris Smowton --- .../CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql b/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql index b5ac2cabd7e..5977d29f26e 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql @@ -69,8 +69,8 @@ private class EncryptedBlobClientBuilderSafeEncryptionVersionConfig extends Data } /** - * Holds if the call `call` is an object creation for a class `EncryptedBlobClientBuilder` - * that takes `versionArg` as the argument for the version, and the version number is safe + * Holds if `call` is an object creation for a class `EncryptedBlobClientBuilder` + * that takes `versionArg` as the argument specifying the encryption version, and that version is safe. */ predicate isCreatingSafeAzureClientSideEncryptionObject(Call call, Class c, Expr versionArg) { isCreatingAzureClientSideEncryptionObjectNewVersion(call, c, versionArg) and From f29104cccef4a628e536e7a82b584bf71962550a Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 12 Jul 2022 14:29:53 +0100 Subject: [PATCH 200/505] C++: Accept test results. --- .../semmle/tests/DangerousUseMbtowc.expected | 34 ++++++++++++++----- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousUseMbtowc.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousUseMbtowc.expected index 89feb7bf82e..0d156d9bf3a 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousUseMbtowc.expected +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousUseMbtowc.expected @@ -1,8 +1,26 @@ -| test.cpp:66:27:66:32 | call to mbtowc | Size can be less than maximum character length, use macro MB_CUR_MAX. | -| test.cpp:76:27:76:32 | call to mbtowc | Size can be less than maximum character length, use macro MB_CUR_MAX. | -| test.cpp:106:11:106:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer. | -| test.cpp:123:11:123:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer. | -| test.cpp:140:11:140:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer. | -| test.cpp:158:11:158:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer. | -| test.cpp:181:11:181:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer. | -| test.cpp:197:11:197:16 | call to mbtowc | Maybe you're using the function's return value incorrectly. | +| test1.cpp:28:5:28:23 | call to WideCharToMultiByte | According to the definition of the functions, if the source buffer and the destination buffer are the same, undefined behavior is possible.badTest1 | +| test1.cpp:29:5:29:23 | call to MultiByteToWideChar | According to the definition of the functions, if the source buffer and the destination buffer are the same, undefined behavior is possible.badTest1 | +| test1.cpp:45:3:45:21 | call to WideCharToMultiByte | According to the definition of the functions, it is not guaranteed to write a null character at the end of the string, so access beyond the bounds of the destination buffer is possible.badTest2 | +| test1.cpp:58:3:58:21 | call to MultiByteToWideChar | The buffer destination has a type other than char, you need to take this into account when allocating memory.badTest3 | +| test1.cpp:70:3:70:21 | call to MultiByteToWideChar | The buffer destination has a type other than char, you need to take this into account when allocating memory.badTest4 | +| test1.cpp:76:10:76:28 | call to WideCharToMultiByte | If the destination buffer is NULL and its size is not 0, then undefined behavior is possible.badTest5 | +| test1.cpp:93:5:93:23 | call to WideCharToMultiByte | According to the definition of the functions, it is not guaranteed to write a null character at the end of the string, so access beyond the bounds of the destination buffer is possible.badTest6 | +| test2.cpp:15:5:15:12 | call to mbstowcs | According to the definition of the functions, if the source buffer and the destination buffer are the same, undefined behavior is possible.badTest1 | +| test2.cpp:17:5:17:15 | call to _mbstowcs_l | According to the definition of the functions, if the source buffer and the destination buffer are the same, undefined behavior is possible.badTest1 | +| test2.cpp:19:5:19:13 | call to mbsrtowcs | According to the definition of the functions, if the source buffer and the destination buffer are the same, undefined behavior is possible.badTest1 | +| test2.cpp:35:3:35:10 | call to mbstowcs | According to the definition of the functions, it is not guaranteed to write a null character at the end of the string, so access beyond the bounds of the destination buffer is possible.badTest2 | +| test2.cpp:48:3:48:10 | call to mbstowcs | The buffer destination has a type other than char, you need to take this into account when allocating memory.badTest3 | +| test2.cpp:60:3:60:10 | call to mbstowcs | The buffer destination has a type other than char, you need to take this into account when allocating memory.badTest4 | +| test2.cpp:66:10:66:17 | call to mbstowcs | If the destination buffer is NULL and its size is not 0, then undefined behavior is possible.badTest5 | +| test2.cpp:80:3:80:10 | call to mbstowcs | According to the definition of the functions, it is not guaranteed to write a null character at the end of the string, so access beyond the bounds of the destination buffer is possible.badTest6 | +| test3.cpp:16:5:16:13 | access to array | This buffer may contain multibyte characters, so attempting to copy may result in part of the last character being lost.badTest1 | +| test3.cpp:36:13:36:18 | ... + ... | This buffer may contain multibyte characters, so an attempt to copy may result in an overflow.badTest2 | +| test3.cpp:47:3:47:24 | access to array | The size of the array element is greater than one byte, so the offset will point outside the array.badTest3 | +| test.cpp:66:27:66:32 | call to mbtowc | Size can be less than maximum character length, use macro MB_CUR_MAX.badTest1 | +| test.cpp:76:27:76:32 | call to mbtowc | Size can be less than maximum character length, use macro MB_CUR_MAX.badTest2 | +| test.cpp:106:11:106:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer.badTest3 | +| test.cpp:123:11:123:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer.badTest4 | +| test.cpp:140:11:140:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer.badTest5 | +| test.cpp:158:11:158:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer.badTest6 | +| test.cpp:181:11:181:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer.badTest7 | +| test.cpp:197:11:197:16 | call to mbtowc | Maybe you're using the function's return value incorrectly.badTest8 | From 83edb3b5e9887d34d817f5547e39418ac0d70d26 Mon Sep 17 00:00:00 2001 From: Ian Lynagh Date: Tue, 12 Jul 2022 17:43:50 +0100 Subject: [PATCH 201/505] Kotlin: Remove the last uses of fakeLabel --- .../src/main/kotlin/KotlinUsesExtractor.kt | 15 ++++++++++----- java/kotlin-extractor/src/main/kotlin/Label.kt | 12 ------------ 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt index 69ddc410799..22bae8b55be 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt @@ -121,14 +121,19 @@ open class KotlinUsesExtractor( } } - private fun extractErrorType(): TypeResults { + private fun extractJavaErrorType(): TypeResult { val typeId = tw.getLabelFor("@\"errorType\"") { tw.writeError_type(it) } + return TypeResult(typeId, null, "") + } + + private fun extractErrorType(): TypeResults { + val javaResult = extractJavaErrorType() val kotlinTypeId = tw.getLabelFor("@\"errorKotlinType\"") { - tw.writeKt_nullable_types(it, typeId) + tw.writeKt_nullable_types(it, javaResult.id) } - return TypeResults(TypeResult(typeId, null, ""), + return TypeResults(javaResult, TypeResult(kotlinTypeId, null, "")) } @@ -719,7 +724,7 @@ open class KotlinUsesExtractor( } else -> { logger.error("Unrecognised IrSimpleType: " + s.javaClass + ": " + s.render()) - return TypeResults(TypeResult(fakeLabel(), "unknown", "unknown"), TypeResult(fakeLabel(), "unknown", "unknown")) + return extractErrorType() } } } @@ -1276,7 +1281,7 @@ open class KotlinUsesExtractor( } else -> { logger.error("Unexpected type argument.") - return TypeResult(fakeLabel(), "unknown", "unknown") + return extractJavaErrorType() } } } diff --git a/java/kotlin-extractor/src/main/kotlin/Label.kt b/java/kotlin-extractor/src/main/kotlin/Label.kt index 24e9a8f691c..10459319a76 100644 --- a/java/kotlin-extractor/src/main/kotlin/Label.kt +++ b/java/kotlin-extractor/src/main/kotlin/Label.kt @@ -29,15 +29,3 @@ class IntLabel(val i: Int): Label { class StringLabel(val name: String): Label { override fun toString(): String = "#$name" } - -// TODO: Remove this and all of its uses -fun fakeLabel(): Label { - if (false) { - println("Fake label") - } else { - val sw = StringWriter() - Exception().printStackTrace(PrintWriter(sw)) - println("Fake label from:\n$sw") - } - return IntLabel(0) -} From 98af52fba5ba5a55436045fe9de78c4d635a26c1 Mon Sep 17 00:00:00 2001 From: ihsinme Date: Tue, 12 Jul 2022 20:19:59 +0300 Subject: [PATCH 202/505] Update DangerousUseMbtowc.ql --- .../src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.ql b/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.ql index 7b17e30e24e..6c0bf84e81b 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.ql @@ -234,4 +234,4 @@ where findUseMultibyteCharacter(exp, msg) or findUseStringConversion(exp, msg, 1, 0, 2, ["mbstowcs", "_mbstowcs_l", "mbsrtowcs"]) or findUseStringConversion(exp, msg, 2, 4, 5, ["MultiByteToWideChar", "WideCharToMultiByte"]) -select exp, msg + exp.getEnclosingFunction().getName() +select exp, msg From e77a9891337df09bafd6fbd5d2cf24960d0b3242 Mon Sep 17 00:00:00 2001 From: ihsinme Date: Tue, 12 Jul 2022 20:22:31 +0300 Subject: [PATCH 203/505] Update DangerousUseMbtowc.expected --- .../semmle/tests/DangerousUseMbtowc.expected | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousUseMbtowc.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousUseMbtowc.expected index 0d156d9bf3a..6766b6c46a4 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousUseMbtowc.expected +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousUseMbtowc.expected @@ -1,26 +1,26 @@ -| test1.cpp:28:5:28:23 | call to WideCharToMultiByte | According to the definition of the functions, if the source buffer and the destination buffer are the same, undefined behavior is possible.badTest1 | -| test1.cpp:29:5:29:23 | call to MultiByteToWideChar | According to the definition of the functions, if the source buffer and the destination buffer are the same, undefined behavior is possible.badTest1 | -| test1.cpp:45:3:45:21 | call to WideCharToMultiByte | According to the definition of the functions, it is not guaranteed to write a null character at the end of the string, so access beyond the bounds of the destination buffer is possible.badTest2 | -| test1.cpp:58:3:58:21 | call to MultiByteToWideChar | The buffer destination has a type other than char, you need to take this into account when allocating memory.badTest3 | -| test1.cpp:70:3:70:21 | call to MultiByteToWideChar | The buffer destination has a type other than char, you need to take this into account when allocating memory.badTest4 | -| test1.cpp:76:10:76:28 | call to WideCharToMultiByte | If the destination buffer is NULL and its size is not 0, then undefined behavior is possible.badTest5 | -| test1.cpp:93:5:93:23 | call to WideCharToMultiByte | According to the definition of the functions, it is not guaranteed to write a null character at the end of the string, so access beyond the bounds of the destination buffer is possible.badTest6 | -| test2.cpp:15:5:15:12 | call to mbstowcs | According to the definition of the functions, if the source buffer and the destination buffer are the same, undefined behavior is possible.badTest1 | -| test2.cpp:17:5:17:15 | call to _mbstowcs_l | According to the definition of the functions, if the source buffer and the destination buffer are the same, undefined behavior is possible.badTest1 | -| test2.cpp:19:5:19:13 | call to mbsrtowcs | According to the definition of the functions, if the source buffer and the destination buffer are the same, undefined behavior is possible.badTest1 | -| test2.cpp:35:3:35:10 | call to mbstowcs | According to the definition of the functions, it is not guaranteed to write a null character at the end of the string, so access beyond the bounds of the destination buffer is possible.badTest2 | -| test2.cpp:48:3:48:10 | call to mbstowcs | The buffer destination has a type other than char, you need to take this into account when allocating memory.badTest3 | -| test2.cpp:60:3:60:10 | call to mbstowcs | The buffer destination has a type other than char, you need to take this into account when allocating memory.badTest4 | -| test2.cpp:66:10:66:17 | call to mbstowcs | If the destination buffer is NULL and its size is not 0, then undefined behavior is possible.badTest5 | -| test2.cpp:80:3:80:10 | call to mbstowcs | According to the definition of the functions, it is not guaranteed to write a null character at the end of the string, so access beyond the bounds of the destination buffer is possible.badTest6 | -| test3.cpp:16:5:16:13 | access to array | This buffer may contain multibyte characters, so attempting to copy may result in part of the last character being lost.badTest1 | -| test3.cpp:36:13:36:18 | ... + ... | This buffer may contain multibyte characters, so an attempt to copy may result in an overflow.badTest2 | -| test3.cpp:47:3:47:24 | access to array | The size of the array element is greater than one byte, so the offset will point outside the array.badTest3 | -| test.cpp:66:27:66:32 | call to mbtowc | Size can be less than maximum character length, use macro MB_CUR_MAX.badTest1 | -| test.cpp:76:27:76:32 | call to mbtowc | Size can be less than maximum character length, use macro MB_CUR_MAX.badTest2 | -| test.cpp:106:11:106:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer.badTest3 | -| test.cpp:123:11:123:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer.badTest4 | -| test.cpp:140:11:140:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer.badTest5 | -| test.cpp:158:11:158:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer.badTest6 | -| test.cpp:181:11:181:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer.badTest7 | -| test.cpp:197:11:197:16 | call to mbtowc | Maybe you're using the function's return value incorrectly.badTest8 | +| test1.cpp:28:5:28:23 | call to WideCharToMultiByte | According to the definition of the functions, if the source buffer and the destination buffer are the same, undefined behavior is possible. | +| test1.cpp:29:5:29:23 | call to MultiByteToWideChar | According to the definition of the functions, if the source buffer and the destination buffer are the same, undefined behavior is possible. | +| test1.cpp:45:3:45:21 | call to WideCharToMultiByte | According to the definition of the functions, it is not guaranteed to write a null character at the end of the string, so access beyond the bounds of the destination buffer is possible. | +| test1.cpp:58:3:58:21 | call to MultiByteToWideChar | The buffer destination has a type other than char, you need to take this into account when allocating memory. | +| test1.cpp:70:3:70:21 | call to MultiByteToWideChar | The buffer destination has a type other than char, you need to take this into account when allocating memory. | +| test1.cpp:76:10:76:28 | call to WideCharToMultiByte | If the destination buffer is NULL and its size is not 0, then undefined behavior is possible. | +| test1.cpp:93:5:93:23 | call to WideCharToMultiByte | According to the definition of the functions, it is not guaranteed to write a null character at the end of the string, so access beyond the bounds of the destination buffer is possible. | +| test2.cpp:15:5:15:12 | call to mbstowcs | According to the definition of the functions, if the source buffer and the destination buffer are the same, undefined behavior is possible. | +| test2.cpp:17:5:17:15 | call to _mbstowcs_l | According to the definition of the functions, if the source buffer and the destination buffer are the same, undefined behavior is possible. | +| test2.cpp:19:5:19:13 | call to mbsrtowcs | According to the definition of the functions, if the source buffer and the destination buffer are the same, undefined behavior is possible. | +| test2.cpp:35:3:35:10 | call to mbstowcs | According to the definition of the functions, it is not guaranteed to write a null character at the end of the string, so access beyond the bounds of the destination buffer is possible. | +| test2.cpp:48:3:48:10 | call to mbstowcs | The buffer destination has a type other than char, you need to take this into account when allocating memory. | +| test2.cpp:60:3:60:10 | call to mbstowcs | The buffer destination has a type other than char, you need to take this into account when allocating memory. | +| test2.cpp:66:10:66:17 | call to mbstowcs | If the destination buffer is NULL and its size is not 0, then undefined behavior is possible. | +| test2.cpp:80:3:80:10 | call to mbstowcs | According to the definition of the functions, it is not guaranteed to write a null character at the end of the string, so access beyond the bounds of the destination buffer is possible. | +| test3.cpp:16:5:16:13 | access to array | This buffer may contain multibyte characters, so attempting to copy may result in part of the last character being lost. | +| test3.cpp:36:13:36:18 | ... + ... | This buffer may contain multibyte characters, so an attempt to copy may result in an overflow. | +| test3.cpp:47:3:47:24 | access to array | The size of the array element is greater than one byte, so the offset will point outside the array. | +| test.cpp:66:27:66:32 | call to mbtowc | Size can be less than maximum character length, use macro MB_CUR_MAX. | +| test.cpp:76:27:76:32 | call to mbtowc | Size can be less than maximum character length, use macro MB_CUR_MAX. | +| test.cpp:106:11:106:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer. | +| test.cpp:123:11:123:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer. | +| test.cpp:140:11:140:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer. | +| test.cpp:158:11:158:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer. | +| test.cpp:181:11:181:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer. | +| test.cpp:197:11:197:16 | call to mbtowc | Maybe you're using the function's return value incorrectly. | From d929b1338b6f1f5a51e50510ed393c6931c9e282 Mon Sep 17 00:00:00 2001 From: Raul Garcia Date: Tue, 12 Jul 2022 11:55:06 -0700 Subject: [PATCH 204/505] Addressing API::Node feedback for all predicates --- ...nsafeUsageOfClientSideEncryptionVersion.ql | 49 ++++++++++++++++--- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql b/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql index 442399e658f..3b35f2350bd 100644 --- a/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql +++ b/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql @@ -14,19 +14,54 @@ import python import semmle.python.ApiGraphs predicate isUnsafeClientSideAzureStorageEncryptionViaAttributes(Call call, AttrNode node) { - exists(ControlFlowNode ctrlFlowNode, AssignStmt astmt, Attribute a | + exists( + API::Node n, API::Node n2, Attribute a, AssignStmt astmt, API::Node uploadBlob, + ControlFlowNode ctrlFlowNode, string s + | + s in ["key_encryption_key", "key_resolver_function"] and + n = + API::moduleImport("azure") + .getMember("storage") + .getMember("blob") + .getMember("BlobClient") + .getReturn() + .getMember(s) and + n2 = + API::moduleImport("azure") + .getMember("storage") + .getMember("blob") + .getMember("BlobClient") + .getReturn() + .getMember("upload_blob") and + n.getAUse().asExpr() = a and astmt.getATarget() = a and - a.getAttr() in ["key_encryption_key", "key_resolver_function"] and a.getAFlowNode() = node and + uploadBlob = + API::moduleImport("azure") + .getMember("storage") + .getMember("blob") + .getMember("BlobClient") + .getReturn() + .getMember("upload_blob") and + uploadBlob.getACall().asExpr() = call and + ctrlFlowNode = call.getAFlowNode() and node.strictlyReaches(ctrlFlowNode) and node != ctrlFlowNode and - call.getAChildNode().(Attribute).getAttr() = "upload_blob" and - ctrlFlowNode = call.getAFlowNode() and - not astmt.getValue() instanceof None and - not exists(AssignStmt astmt2, Attribute a2, AttrNode encryptionVersionSet, StrConst uc | + not exists( + AssignStmt astmt2, Attribute a2, AttrNode encryptionVersionSet, StrConst uc, + API::Node encryptionVersion + | uc = astmt2.getValue() and uc.getText() in ["'2.0'", "2.0"] and - a2.getAttr() = "encryption_version" and + encryptionVersion = + API::moduleImport("azure") + .getMember("storage") + .getMember("blob") + .getMember("BlobClient") + .getReturn() + .getMember("encryption_version") and + encryptionVersion.getAUse().asExpr() = a2 and + astmt2.getATarget() = a2 and a2.getAFlowNode() = encryptionVersionSet and encryptionVersionSet.strictlyReaches(ctrlFlowNode) ) From a4adf067135b6f3573b6093c86572e8ab3b7449d Mon Sep 17 00:00:00 2001 From: Raul Garcia Date: Tue, 12 Jul 2022 13:51:12 -0700 Subject: [PATCH 205/505] Addressing feedback for the qhelp file. --- .../Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp b/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp index 45d919ec702..190ce5e25dc 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp +++ b/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp @@ -4,12 +4,12 @@

    Azure Storage .NET, Java, and Python SDKs support encryption on the client with a customer-managed key that is maintained in Azure Key Vault or another key store.

    -

    Current release versions of the Azure Storage SDKs use cipher block chaining (CBC mode) for client-side encryption (referred to as v1).

    +

    The Azure Storage SDK version 12.18.0 or later supports version V2 for client-side encryption. All previous versions of Azure Storage SDK only support client-side encryption V1 which is unsafe.

    -

    Consider switching to v2 client-side encryption.

    +

    Consider switching to V2 client-side encryption.

    From 7facc63699ed709c25af772c981d2a4aa2d0072b Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Tue, 12 Jul 2022 22:59:48 +0000 Subject: [PATCH 206/505] remove predicate --- .../experimental/weak-params/WeakParams.ql | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/ruby/ql/src/experimental/weak-params/WeakParams.ql b/ruby/ql/src/experimental/weak-params/WeakParams.ql index 6ea73aa42de..86ba72cbd91 100644 --- a/ruby/ql/src/experimental/weak-params/WeakParams.ql +++ b/ruby/ql/src/experimental/weak-params/WeakParams.ql @@ -38,21 +38,16 @@ class ActionControllerRequest extends DataFlow::Node { class WeakParams extends DataFlow::CallNode { WeakParams() { this.getReceiver() instanceof ActionControllerRequest and - allParamsAccess(this.asExpr().getExpr()) + ( + this.getMethodName() = "path_parametes" or + this.getMethodName() = "query_parameters" or + this.getMethodName() = "request_parameters" or + this.getMethodName() = "GET" or + this.getMethodName() = "POST" + ) } } -/** - * Holds call to a method that exposes or accesses all parameters from an inbound HTTP request - */ -predicate allParamsAccess(MethodCall call) { - call.getMethodName() = "path_parametes" or - call.getMethodName() = "query_parameters" or - call.getMethodName() = "request_parameters" or - call.getMethodName() = "GET" or - call.getMethodName() = "POST" -} - /** * A Taint tracking config where the source is a weak params access in a controller and the sink * is a method call of a model class From db5f63b20876b26a73ea829fe3cba206ae26ac95 Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Tue, 12 Jul 2022 23:14:16 +0000 Subject: [PATCH 207/505] add tests --- .../weak-params/WeakParams.expected | 20 +++++++++++++ .../experimental/weak-params/WeakParams.rb | 28 +++++++++++++++++-- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/ruby/ql/test/query-tests/experimental/weak-params/WeakParams.expected b/ruby/ql/test/query-tests/experimental/weak-params/WeakParams.expected index e69de29bb2d..14bd3e4e13f 100644 --- a/ruby/ql/test/query-tests/experimental/weak-params/WeakParams.expected +++ b/ruby/ql/test/query-tests/experimental/weak-params/WeakParams.expected @@ -0,0 +1,20 @@ +edges +| WeakParams.rb:5:28:5:53 | call to request_parameters : | WeakParams.rb:5:28:5:59 | ...[...] | +| WeakParams.rb:10:28:10:51 | call to query_parameters : | WeakParams.rb:10:28:10:57 | ...[...] | +| WeakParams.rb:15:28:15:39 | call to POST : | WeakParams.rb:15:28:15:45 | ...[...] | +| WeakParams.rb:20:28:20:38 | call to GET : | WeakParams.rb:20:28:20:44 | ...[...] | +nodes +| WeakParams.rb:5:28:5:53 | call to request_parameters : | semmle.label | call to request_parameters : | +| WeakParams.rb:5:28:5:59 | ...[...] | semmle.label | ...[...] | +| WeakParams.rb:10:28:10:51 | call to query_parameters : | semmle.label | call to query_parameters : | +| WeakParams.rb:10:28:10:57 | ...[...] | semmle.label | ...[...] | +| WeakParams.rb:15:28:15:39 | call to POST : | semmle.label | call to POST : | +| WeakParams.rb:15:28:15:45 | ...[...] | semmle.label | ...[...] | +| WeakParams.rb:20:28:20:38 | call to GET : | semmle.label | call to GET : | +| WeakParams.rb:20:28:20:44 | ...[...] | semmle.label | ...[...] | +subpaths +#select +| WeakParams.rb:5:28:5:59 | ...[...] | WeakParams.rb:5:28:5:53 | call to request_parameters : | WeakParams.rb:5:28:5:59 | ...[...] | By exposing all keys in request parameters or by blindy accessing them, unintended parameters could be used and lead to mass-assignment or have other unexpected side-effects. It is safer to follow the 'strong parameters' pattern in Rails, which is outlined here: https://api.rubyonrails.org/classes/ActionController/StrongParameters.html | +| WeakParams.rb:10:28:10:57 | ...[...] | WeakParams.rb:10:28:10:51 | call to query_parameters : | WeakParams.rb:10:28:10:57 | ...[...] | By exposing all keys in request parameters or by blindy accessing them, unintended parameters could be used and lead to mass-assignment or have other unexpected side-effects. It is safer to follow the 'strong parameters' pattern in Rails, which is outlined here: https://api.rubyonrails.org/classes/ActionController/StrongParameters.html | +| WeakParams.rb:15:28:15:45 | ...[...] | WeakParams.rb:15:28:15:39 | call to POST : | WeakParams.rb:15:28:15:45 | ...[...] | By exposing all keys in request parameters or by blindy accessing them, unintended parameters could be used and lead to mass-assignment or have other unexpected side-effects. It is safer to follow the 'strong parameters' pattern in Rails, which is outlined here: https://api.rubyonrails.org/classes/ActionController/StrongParameters.html | +| WeakParams.rb:20:28:20:44 | ...[...] | WeakParams.rb:20:28:20:38 | call to GET : | WeakParams.rb:20:28:20:44 | ...[...] | By exposing all keys in request parameters or by blindy accessing them, unintended parameters could be used and lead to mass-assignment or have other unexpected side-effects. It is safer to follow the 'strong parameters' pattern in Rails, which is outlined here: https://api.rubyonrails.org/classes/ActionController/StrongParameters.html | diff --git a/ruby/ql/test/query-tests/experimental/weak-params/WeakParams.rb b/ruby/ql/test/query-tests/experimental/weak-params/WeakParams.rb index 81d57239f29..a5edef2e6dc 100644 --- a/ruby/ql/test/query-tests/experimental/weak-params/WeakParams.rb +++ b/ruby/ql/test/query-tests/experimental/weak-params/WeakParams.rb @@ -1,18 +1,40 @@ class TestController < ActionController::Base + + # Should catch def create - TestObject.new(request.request_parameters) + TestObject.create(foo: request.request_parameters[:foo]) end + # Should catch def create_query - TestObject.new(request.query_parameters) + TestObject.create(foo: request.query_parameters[:foo]) end + # Should catch + def update_unsafe + TestObject.update(foo: request.POST[:foo]) + end + + # Should catch + def update_unsafe_get + TestObject.update(foo: request.GET[:foo]) + end + + # Should not catch def update TestObject.update(object_params) end - # + # strong params method def object_params params.require(:uuid).permit(:notes) end + + # Should not catch + def test_non_sink + puts request.request_parameters + end +end + +class TestObject < ActiveRecord::Base end \ No newline at end of file From b3f1a513d148eac778dfe66afc2c0254591e8b17 Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Wed, 13 Jul 2022 00:25:19 +0000 Subject: [PATCH 208/505] Update tests --- .../ExampleController.rb | 48 ---------- .../ManuallyCheckHttpVerb.rb | 96 +++++++++++++++++++ .../manually-check-http-verb/NotController.rb | 17 ---- 3 files changed, 96 insertions(+), 65 deletions(-) delete mode 100644 ruby/ql/test/query-tests/experimental/manually-check-http-verb/ExampleController.rb create mode 100644 ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.rb delete mode 100644 ruby/ql/test/query-tests/experimental/manually-check-http-verb/NotController.rb diff --git a/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ExampleController.rb b/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ExampleController.rb deleted file mode 100644 index 7e4ab4a1a77..00000000000 --- a/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ExampleController.rb +++ /dev/null @@ -1,48 +0,0 @@ -class ExampleController < ActionController::Base - # This function should have 6 vulnerable lines - def example_action - if request.get? - Example.find(params[:example_id]) - end - end -end - -class OtherController < ActionController::Base - def other_action - if env['REQUEST_METHOD'] == "GET" - Other.find(params[:id]) - end - end -end - -class ResourceController < ActionController::Base - # This method should have 1 vulnerable line, but is currently failing because it's not a comparison node - def resource_action - case env['REQUEST_METHOD'] - when "GET" - Resource.find(params[:id]) - when "POST" - Resource.new(params[:id], params[:details]) - end - end -end - -class SafeController < ActionController::Base - # this method should have no hits because controllers rely on conventional Rails routes - def index - Safe.find(params[:id]) - end - - def create - Safe.new(params[:id], params[:details]) - end - - def update - Safe.update(params[:id], params[:details]) - end - - def delete - s = Safe.find(params[:id]) - s.delete - end -end \ No newline at end of file diff --git a/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.rb b/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.rb new file mode 100644 index 00000000000..aa10ebc69aa --- /dev/null +++ b/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.rb @@ -0,0 +1,96 @@ +class ExampleController < ActionController::Base + # Should find + def example_action + if request.get? + Resource.find(id: params[:example_id]) + end + end + + # Should find + def other_action + if request.env['REQUEST_METHOD'] == "GET" + Resource.find(id: params[:id]) + end + end + + # Should find + def foo + if request.request_method == "GET" + Resource.find(id: params[:id]) + end + end + + # Should find + def bar + if request.method == "GET" + Resource.find(id: params[:id]) + end + end + + # Should find + def baz + if request.raw_request_method == "GET" + Resource.find(id: params[:id]) + end + end + + # Should find + def foobarbaz + if request.request_method_symbol == :GET + Resource.find(id: params[:id]) + end + end + + # Should find + def resource_action + case request.env['REQUEST_METHOD'] + when "GET" + Resource.find(id: params[:id]) + when "POST" + Resource.new(id: params[:id], details: params[:details]) + end + end + + +end + +class SafeController < ActionController::Base + # this class should have no hits because controllers rely on conventional Rails routes + def index + Resource.find(id: params[:id]) + end + + def create + Resource.new(id: params[:id], details: params[:details]) + end + + def update + Resource.update(id: params[:id], details: params[:details]) + end + + def delete + s = Resource.find(id: params[:id]) + s.delete + end +end + +# There should be no hits from this class because it does not inherit from ActionController +class NotAController + def example_action + if request.get? + Resource.find(params[:example_id]) + end + end + + def resource_action + case env['REQUEST_METHOD'] + when "GET" + Resource.find(params[:id]) + when "POST" + Resource.new(params[:id], params[:details]) + end + end +end + +class Resource < ActiveRecord::Base +end \ No newline at end of file diff --git a/ruby/ql/test/query-tests/experimental/manually-check-http-verb/NotController.rb b/ruby/ql/test/query-tests/experimental/manually-check-http-verb/NotController.rb deleted file mode 100644 index 78e194245e2..00000000000 --- a/ruby/ql/test/query-tests/experimental/manually-check-http-verb/NotController.rb +++ /dev/null @@ -1,17 +0,0 @@ -# There should be no hits from this class because it does not inherit from ActionController -class NotAController - def example_action - if request.get? - Example.find(params[:example_id]) - end - end - - def resource_action - case env['REQUEST_METHOD'] - when "GET" - Resource.find(params[:id]) - when "POST" - Resource.new(params[:id], params[:details]) - end - end -end \ No newline at end of file From 712900257342ffbfbb788c6491e955c7f10c3dc1 Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Wed, 13 Jul 2022 00:33:58 +0000 Subject: [PATCH 209/505] tweak tests more --- .../ManuallyCheckHttpVerb.ql | 10 +++++++++- .../ManuallyCheckHttpVerb.rb | 15 ++++++++++----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql b/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql index 5e1db6f0de7..72574e148d2 100644 --- a/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql +++ b/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql @@ -18,7 +18,15 @@ import codeql.ruby.frameworks.ActionController class Request extends DataFlow::CallNode { Request() { this.getMethodName() = "request" and - this.asExpr().getExpr() instanceof ActionControllerActionMethod + this.asExpr().getExpr().getEnclosingMethod() instanceof ActionControllerActionMethod + } +} + +// `request.env` +class RequestEnvMethod extends DataFlow::CallNode { + RequestEnvMethod() { + this.getMethodName() = "env" and + any(Request r).flowsTo(this.getReceiver()) } } diff --git a/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.rb b/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.rb index aa10ebc69aa..ad0f5b45566 100644 --- a/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.rb +++ b/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.rb @@ -8,35 +8,40 @@ class ExampleController < ActionController::Base # Should find def other_action - if request.env['REQUEST_METHOD'] == "GET" + method = request.env['REQUEST_METHOD'] + if method == "GET" Resource.find(id: params[:id]) end end # Should find def foo - if request.request_method == "GET" + method = request.request_method + if method == "GET" Resource.find(id: params[:id]) end end # Should find def bar - if request.method == "GET" + method = request.method + if method == "GET" Resource.find(id: params[:id]) end end # Should find def baz - if request.raw_request_method == "GET" + method = request.raw_request_method + if method == "GET" Resource.find(id: params[:id]) end end # Should find def foobarbaz - if request.request_method_symbol == :GET + method = request.request_method_symbol + if method == :GET Resource.find(id: params[:id]) end end From 0dbb03f732b5b5fcbdf54555331f4f014925e92f Mon Sep 17 00:00:00 2001 From: Raul Garcia Date: Tue, 12 Jul 2022 21:49:19 -0700 Subject: [PATCH 210/505] Adding CVE information. --- .../Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp | 2 +- .../CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql | 2 +- .../Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp | 2 +- .../CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql | 2 +- .../Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp b/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp index e0ee017cb14..54c9a4998b4 100644 --- a/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp +++ b/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp @@ -22,7 +22,7 @@ Azure Storage Client Encryption Blog.
  • - CVE-2022-PENDING + CVE-2022-30187
  • diff --git a/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql b/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql index b150ca71060..eb1cb673ed2 100644 --- a/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql +++ b/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql @@ -1,5 +1,5 @@ /** - * @name Unsafe usage of v1 version of Azure Storage client-side encryption (CVE-2022-PENDING). + * @name Unsafe usage of v1 version of Azure Storage client-side encryption (CVE-2022-30187). * @description Unsafe usage of v1 version of Azure Storage client-side encryption, please refer to http://aka.ms/azstorageclientencryptionblog * @kind problem * @tags security diff --git a/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp b/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp index 190ce5e25dc..b6884aed914 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp +++ b/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp @@ -22,7 +22,7 @@ Azure Storage Client Encryption Blog.
  • - CVE-2022-PENDING + CVE-2022-30187
  • diff --git a/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql b/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql index 5977d29f26e..ba78fa56423 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql @@ -1,5 +1,5 @@ /** - * @name Unsafe usage of v1 version of Azure Storage client-side encryption (CVE-2022-PENDING). + * @name Unsafe usage of v1 version of Azure Storage client-side encryption (CVE-2022-30187). * @description Unsafe usage of v1 version of Azure Storage client-side encryption, please refer to http://aka.ms/azstorageclientencryptionblog * @kind problem * @tags security diff --git a/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp b/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp index eaf49f371e6..a2f9a2213c3 100644 --- a/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp +++ b/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp @@ -22,7 +22,7 @@ Azure Storage Client Encryption Blog.
  • - CVE-2022-PENDING + CVE-2022-30187
  • From 706d1d2eee83ed3b71545801c080e1688469b839 Mon Sep 17 00:00:00 2001 From: Harry Maclean Date: Thu, 12 May 2022 15:12:18 +0100 Subject: [PATCH 211/505] Ruby: Make StringArrayInclusion more sensitive We now recognise the following pattern as a barrier guard for `x`: values = ["foo", "bar"] if values.include? x sink x end --- .../lib/codeql/ruby/controlflow/CfgNodes.qll | 3 + .../codeql/ruby/dataflow/BarrierGuards.qll | 25 +++++-- .../barrier-guards/barrier-guards.expected | 8 ++ .../dataflow/barrier-guards/barrier-guards.rb | 73 +++++++++++++++++++ 4 files changed, 103 insertions(+), 6 deletions(-) create mode 100644 ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.expected create mode 100644 ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.rb diff --git a/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll b/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll index efb69af39eb..29668b82e70 100644 --- a/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll +++ b/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll @@ -374,6 +374,9 @@ module ExprNodes { MethodCallCfgNode() { super.getExpr() instanceof MethodCall } override MethodCall getExpr() { result = super.getExpr() } + + /** Gets the name of this method call. */ + string getMethodName() { result = this.getExpr().getMethodName() } } private class CaseExprChildMapping extends ExprChildMapping, CaseExpr { diff --git a/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll b/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll index d55bd9af278..506f95b5b45 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll @@ -3,6 +3,8 @@ private import ruby private import codeql.ruby.DataFlow private import codeql.ruby.CFG +private import codeql.ruby.controlflow.CfgNodes +private import codeql.ruby.dataflow.SSA private predicate stringConstCompare(CfgNodes::ExprCfgNode g, CfgNode e, boolean branch) { exists(CfgNodes::ExprNodes::ComparisonOperationCfgNode c | @@ -133,13 +135,24 @@ deprecated class StringConstArrayInclusionCall extends DataFlow::BarrierGuard, private CfgNode checkedNode; StringConstArrayInclusionCall() { - exists(ArrayLiteral aLit | - this.getExpr().getMethodName() = "include?" and - [this.getExpr().getReceiver(), this.getExpr().getReceiver().(ConstantReadAccess).getValue()] = - aLit + this.getMethodName() = "include?" and + this.getArgument(0) = checkedNode and + exists(ExprNodes::ArrayLiteralCfgNode arr | + // [...].include? + this.getReceiver() = arr + or + // C = [...] + // C.include? + this.getReceiver().(ExprNodes::ConstantReadAccessCfgNode).getExpr().getValue().getDesugared() = + arr.getExpr() + or + // x = [...] + // x.include? + exists(Ssa::WriteDefinition def | def.getARead() = this.getReceiver() and def.assigns(arr)) | - forall(Expr elem | elem = aLit.getAnElement() | elem instanceof StringLiteral) and - this.getArgument(0) = checkedNode + forall(ExprCfgNode elem | elem = arr.getAnArgument() | + elem instanceof ExprNodes::StringLiteralCfgNode + ) ) } diff --git a/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.expected b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.expected new file mode 100644 index 00000000000..dcbe2c76d80 --- /dev/null +++ b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.expected @@ -0,0 +1,8 @@ +| barrier-guards.rb:3:4:3:15 | ... == ... | barrier-guards.rb:4:5:4:7 | foo | barrier-guards.rb:3:4:3:6 | foo | true | +| barrier-guards.rb:9:4:9:24 | call to include? | barrier-guards.rb:10:5:10:7 | foo | barrier-guards.rb:9:21:9:23 | foo | true | +| barrier-guards.rb:15:4:15:15 | ... != ... | barrier-guards.rb:18:5:18:7 | foo | barrier-guards.rb:15:4:15:6 | foo | false | +| barrier-guards.rb:21:8:21:19 | ... == ... | barrier-guards.rb:24:5:24:7 | foo | barrier-guards.rb:21:8:21:10 | foo | true | +| barrier-guards.rb:27:8:27:19 | ... != ... | barrier-guards.rb:28:5:28:7 | foo | barrier-guards.rb:27:8:27:10 | foo | false | +| barrier-guards.rb:37:4:37:20 | call to include? | barrier-guards.rb:38:5:38:7 | foo | barrier-guards.rb:37:17:37:19 | foo | true | +| barrier-guards.rb:43:4:43:15 | ... == ... | barrier-guards.rb:45:9:45:11 | foo | barrier-guards.rb:43:4:43:6 | foo | true | +| barrier-guards.rb:69:4:69:21 | call to include? | barrier-guards.rb:70:5:70:7 | foo | barrier-guards.rb:69:18:69:20 | foo | true | diff --git a/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.rb b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.rb new file mode 100644 index 00000000000..78ef5e7bf19 --- /dev/null +++ b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.rb @@ -0,0 +1,73 @@ +foo = "foo" + +if foo == "foo" + foo +else + foo +end + +if ["foo"].include?(foo) + foo +else + foo +end + +if foo != "foo" + foo +else + foo +end + +unless foo == "foo" + foo +else + foo +end + +unless foo != "foo" + foo +else + foo +end + +foo + +FOO = ["foo"] + +if FOO.include?(foo) + foo +else + foo +end + +if foo == "foo" + capture { + foo # guarded + } +end + +if foo == "foo" + capture { + foo = "bar" + foo # not guarded + } +end + +if foo == "foo" + my_lambda = -> () { + foo # not guarded + } + + foo = "bar" + + my_lambda() +end + +foos = ["foo"] +bars = ["bar"] + +if foos.include?(foo) + foo +else + foo +end \ No newline at end of file From 301914d80c41db9f662740171755a391228ea448 Mon Sep 17 00:00:00 2001 From: Harry Maclean Date: Thu, 12 May 2022 15:41:55 +0100 Subject: [PATCH 212/505] Ruby: Add an extra barrier guard test --- .../barrier-guards/barrier-guards.expected | 2 +- .../dataflow/barrier-guards/barrier-guards.rb | 23 +++++++++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.expected b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.expected index dcbe2c76d80..fcf95346a5a 100644 --- a/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.expected +++ b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.expected @@ -5,4 +5,4 @@ | barrier-guards.rb:27:8:27:19 | ... != ... | barrier-guards.rb:28:5:28:7 | foo | barrier-guards.rb:27:8:27:10 | foo | false | | barrier-guards.rb:37:4:37:20 | call to include? | barrier-guards.rb:38:5:38:7 | foo | barrier-guards.rb:37:17:37:19 | foo | true | | barrier-guards.rb:43:4:43:15 | ... == ... | barrier-guards.rb:45:9:45:11 | foo | barrier-guards.rb:43:4:43:6 | foo | true | -| barrier-guards.rb:69:4:69:21 | call to include? | barrier-guards.rb:70:5:70:7 | foo | barrier-guards.rb:69:18:69:20 | foo | true | +| barrier-guards.rb:70:4:70:21 | call to include? | barrier-guards.rb:71:5:71:7 | foo | barrier-guards.rb:70:18:70:20 | foo | true | diff --git a/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.rb b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.rb index 78ef5e7bf19..a3f13b48639 100644 --- a/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.rb +++ b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.rb @@ -63,11 +63,30 @@ if foo == "foo" my_lambda() end +foos = nil foos = ["foo"] -bars = ["bar"] +bars = NotAnArray.new if foos.include?(foo) foo else foo -end \ No newline at end of file +end + +if bars.include?(foo) + foo +else + foo +end + +bars = ["bar"] + +if condition + bars = nil +end + +if bars.include?(foo) + foo +else + foo +end From b5a3d3c4886dce9892ec25eacf474084a0d85463 Mon Sep 17 00:00:00 2001 From: Harry Maclean Date: Mon, 23 May 2022 13:34:06 +0100 Subject: [PATCH 213/505] Ruby: Extract isArrayConstant This predicate might be useful elsewhere. --- .../lib/codeql/ruby/ast/internal/Constant.qll | 20 +++++++++++++++++++ .../codeql/ruby/dataflow/BarrierGuards.qll | 15 ++------------ 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/ast/internal/Constant.qll b/ruby/ql/lib/codeql/ruby/ast/internal/Constant.qll index fb7cb3737b9..00e4472c7c1 100644 --- a/ruby/ql/lib/codeql/ruby/ast/internal/Constant.qll +++ b/ruby/ql/lib/codeql/ruby/ast/internal/Constant.qll @@ -509,3 +509,23 @@ private module Cached { } import Cached + +/** + * Holds if `e` is an array constructed from an array literal. + * Example: + * ```rb + * [1, 2, 3] + * C = [1, 2, 3]; C + * x = [1, 2, 3]; x + * ``` + */ +predicate isArrayConstant(ExprCfgNode e, ArrayLiteralCfgNode arr) { + // [...] + e = arr + or + // C = [...]; C + e.(ExprNodes::ConstantReadAccessCfgNode).getExpr().getValue().getDesugared() = arr.getExpr() + or + // x = [...]; x + exists(Ssa::WriteDefinition def | def.getARead() = e and def.assigns(arr)) +} diff --git a/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll b/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll index 506f95b5b45..52e9c48779d 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll @@ -5,6 +5,7 @@ private import codeql.ruby.DataFlow private import codeql.ruby.CFG private import codeql.ruby.controlflow.CfgNodes private import codeql.ruby.dataflow.SSA +private import codeql.ruby.ast.internal.Constant private predicate stringConstCompare(CfgNodes::ExprCfgNode g, CfgNode e, boolean branch) { exists(CfgNodes::ExprNodes::ComparisonOperationCfgNode c | @@ -137,19 +138,7 @@ deprecated class StringConstArrayInclusionCall extends DataFlow::BarrierGuard, StringConstArrayInclusionCall() { this.getMethodName() = "include?" and this.getArgument(0) = checkedNode and - exists(ExprNodes::ArrayLiteralCfgNode arr | - // [...].include? - this.getReceiver() = arr - or - // C = [...] - // C.include? - this.getReceiver().(ExprNodes::ConstantReadAccessCfgNode).getExpr().getValue().getDesugared() = - arr.getExpr() - or - // x = [...] - // x.include? - exists(Ssa::WriteDefinition def | def.getARead() = this.getReceiver() and def.assigns(arr)) - | + exists(ExprNodes::ArrayLiteralCfgNode arr | isArrayConstant(this.getReceiver(), arr) | forall(ExprCfgNode elem | elem = arr.getAnArgument() | elem instanceof ExprNodes::StringLiteralCfgNode ) From 63dcce9a31dbd5af563b970308e3108712232bf1 Mon Sep 17 00:00:00 2001 From: Harry Maclean Date: Thu, 30 Jun 2022 16:15:56 +1200 Subject: [PATCH 214/505] Ruby: Refactor isArrayConstant --- .../lib/codeql/ruby/ast/internal/Constant.qll | 37 ++++++++++++++++--- .../ast/constants/constants.expected | 16 ++++++++ .../library-tests/ast/constants/constants.ql | 3 ++ .../library-tests/ast/constants/constants.rb | 8 ++++ 4 files changed, 58 insertions(+), 6 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/ast/internal/Constant.qll b/ruby/ql/lib/codeql/ruby/ast/internal/Constant.qll index 00e4472c7c1..ae1d9f5989c 100644 --- a/ruby/ql/lib/codeql/ruby/ast/internal/Constant.qll +++ b/ruby/ql/lib/codeql/ruby/ast/internal/Constant.qll @@ -36,7 +36,7 @@ private import ExprNodes * constant value in some cases. */ private module Propagation { - private ExprCfgNode getSource(VariableReadAccessCfgNode read) { + ExprCfgNode getSource(VariableReadAccessCfgNode read) { exists(Ssa::WriteDefinition def | def.assigns(result) and read = def.getARead() @@ -511,7 +511,8 @@ private module Cached { import Cached /** - * Holds if `e` is an array constructed from an array literal. + * Holds if the control flow node `e` refers to an array constructed from the + * array literal `arr`. * Example: * ```rb * [1, 2, 3] @@ -523,9 +524,33 @@ predicate isArrayConstant(ExprCfgNode e, ArrayLiteralCfgNode arr) { // [...] e = arr or - // C = [...]; C - e.(ExprNodes::ConstantReadAccessCfgNode).getExpr().getValue().getDesugared() = arr.getExpr() + // e = [...]; e + isArrayConstant(getSource(e), arr) or - // x = [...]; x - exists(Ssa::WriteDefinition def | def.getARead() = e and def.assigns(arr)) + isArrayExpr(e.getExpr(), arr) +} + +/** + * Holds if the expression `e` refers to an array constructed from the array literal `arr`. + */ +predicate isArrayExpr(Expr e, ArrayLiteralCfgNode arr) { + // e = [...] + e = arr.getExpr() + or + // Like above, but handles the desugaring of array literals to Array.[] calls. + e.getDesugared() = arr.getExpr() + or + // A = [...]; A + // A = a; A + isArrayExpr(e.(ConstantReadAccess).getValue(), arr) + or + // Recurse via CFG nodes. Necessary for example in: + // a = [...] + // A = a + // A + // + // We map from A to a via ConstantReadAccess::getValue, yielding the Expr a. + // To get to [...] we need to go via getSource(ExprCfgNode e), so we find a + // CFG node for a and call `isArrayConstant`. + isArrayConstant(e.getAControlFlowNode(), arr) } diff --git a/ruby/ql/test/library-tests/ast/constants/constants.expected b/ruby/ql/test/library-tests/ast/constants/constants.expected index 89b5512921c..0fe1b324232 100644 --- a/ruby/ql/test/library-tests/ast/constants/constants.expected +++ b/ruby/ql/test/library-tests/ast/constants/constants.expected @@ -61,6 +61,13 @@ constantAccess | constants.rb:71:5:71:14 | FOURTY_SIX | write | FOURTY_SIX | ConstantAssignment | | constants.rb:73:18:73:21 | Mod3 | read | Mod3 | ConstantReadAccess | | constants.rb:73:18:73:33 | FOURTY_SIX | read | FOURTY_SIX | ConstantReadAccess | +| constants.rb:78:5:78:13 | Array | read | Array | ConstantReadAccess | +| constants.rb:79:1:79:1 | A | write | A | ConstantAssignment | +| constants.rb:79:5:79:13 | Array | read | Array | ConstantReadAccess | +| constants.rb:80:1:80:1 | B | write | B | ConstantAssignment | +| constants.rb:81:1:81:1 | C | write | C | ConstantAssignment | +| constants.rb:81:5:81:5 | A | read | A | ConstantReadAccess | +| constants.rb:82:5:82:5 | B | read | B | ConstantReadAccess | getConst | constants.rb:1:1:15:3 | ModuleA | CONST_B | constants.rb:6:15:6:23 | "const_b" | | constants.rb:1:1:15:3 | ModuleA | FOURTY_FOUR | constants.rb:53:17:53:29 | "fourty-four" | @@ -133,3 +140,12 @@ constantWriteAccessQualifiedName | constants.rb:70:3:72:5 | Mod5 | Mod3::Mod5 | | constants.rb:71:5:71:14 | FOURTY_SIX | Mod1::Mod3::Mod5::FOURTY_SIX | | constants.rb:71:5:71:14 | FOURTY_SIX | Mod3::Mod5::FOURTY_SIX | +| constants.rb:79:1:79:1 | A | A | +| constants.rb:80:1:80:1 | B | B | +| constants.rb:81:1:81:1 | C | C | +arrayConstant +| constants.rb:20:13:20:37 | call to [] | constants.rb:20:13:20:37 | call to [] | +| constants.rb:78:5:78:13 | call to [] | constants.rb:78:5:78:13 | call to [] | +| constants.rb:79:5:79:13 | call to [] | constants.rb:79:5:79:13 | call to [] | +| constants.rb:80:5:80:5 | a | constants.rb:78:5:78:13 | call to [] | +| constants.rb:81:5:81:5 | A | constants.rb:79:5:79:13 | call to [] | diff --git a/ruby/ql/test/library-tests/ast/constants/constants.ql b/ruby/ql/test/library-tests/ast/constants/constants.ql index 66c0d230d99..37140a957a3 100644 --- a/ruby/ql/test/library-tests/ast/constants/constants.ql +++ b/ruby/ql/test/library-tests/ast/constants/constants.ql @@ -1,5 +1,6 @@ import ruby import codeql.ruby.ast.internal.Module as M +import codeql.ruby.ast.internal.Constant query predicate constantAccess(ConstantAccess a, string kind, string name, string cls) { ( @@ -20,3 +21,5 @@ query predicate constantValue(ConstantReadAccess a, Expr e) { e = a.getValue() } query predicate constantWriteAccessQualifiedName(ConstantWriteAccess w, string qualifiedName) { w.getAQualifiedName() = qualifiedName } + +query predicate arrayConstant = isArrayConstant/2; diff --git a/ruby/ql/test/library-tests/ast/constants/constants.rb b/ruby/ql/test/library-tests/ast/constants/constants.rb index 08e0d4216a9..fce55bccd00 100644 --- a/ruby/ql/test/library-tests/ast/constants/constants.rb +++ b/ruby/ql/test/library-tests/ast/constants/constants.rb @@ -72,3 +72,11 @@ module Mod4 end @@fourty_six = Mod3::FOURTY_SIX end + +# Array constants + +a = [1, 2, 3] +A = [1, 2, 3] +B = a +C = A +b = B \ No newline at end of file From 5f17d8370c1b8f3b547ba290c970bd5e8691952d Mon Sep 17 00:00:00 2001 From: Harry Maclean Date: Tue, 12 Jul 2022 13:19:13 +1200 Subject: [PATCH 215/505] Ruby: Small change to isArrayExpr --- .../lib/codeql/ruby/ast/internal/Constant.qll | 7 +++++- .../ast/constants/constants.expected | 22 +++++++++++++++++++ .../library-tests/ast/constants/constants.rb | 7 +++++- 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/ast/internal/Constant.qll b/ruby/ql/lib/codeql/ruby/ast/internal/Constant.qll index ae1d9f5989c..e7e303a9fa6 100644 --- a/ruby/ql/lib/codeql/ruby/ast/internal/Constant.qll +++ b/ruby/ql/lib/codeql/ruby/ast/internal/Constant.qll @@ -552,5 +552,10 @@ predicate isArrayExpr(Expr e, ArrayLiteralCfgNode arr) { // We map from A to a via ConstantReadAccess::getValue, yielding the Expr a. // To get to [...] we need to go via getSource(ExprCfgNode e), so we find a // CFG node for a and call `isArrayConstant`. - isArrayConstant(e.getAControlFlowNode(), arr) + // + // The use of `forex` is intended to ensure that a is an array constant in all + // control flow paths. + // Note(hmac): I don't think this is necessary, as `getSource` will not return + // results if the source is a phi node. + forex(ExprCfgNode n | n = e.getAControlFlowNode() | isArrayConstant(n, arr)) } diff --git a/ruby/ql/test/library-tests/ast/constants/constants.expected b/ruby/ql/test/library-tests/ast/constants/constants.expected index 0fe1b324232..3f189cc26fd 100644 --- a/ruby/ql/test/library-tests/ast/constants/constants.expected +++ b/ruby/ql/test/library-tests/ast/constants/constants.expected @@ -78,23 +78,41 @@ getConst | constants.rb:54:3:58:5 | ModuleA::ModuleB::ClassB | FOURTY_ONE | constants.rb:48:18:48:19 | 41 | | constants.rb:62:3:64:5 | Mod1::Mod3 | FOURTY_FIVE | constants.rb:63:19:63:20 | 45 | | constants.rb:70:3:72:5 | Mod1::Mod3::Mod5 | FOURTY_SIX | constants.rb:71:18:71:19 | 46 | +| file://:0:0:0:0 | Object | A | constants.rb:79:5:79:13 | [...] | +| file://:0:0:0:0 | Object | B | constants.rb:80:5:80:5 | a | +| file://:0:0:0:0 | Object | C | constants.rb:81:5:81:5 | A | | file://:0:0:0:0 | Object | GREETING | constants.rb:17:12:17:64 | ... + ... | lookupConst | constants.rb:1:1:15:3 | ModuleA | CONST_B | constants.rb:6:15:6:23 | "const_b" | | constants.rb:1:1:15:3 | ModuleA | FOURTY_FOUR | constants.rb:53:17:53:29 | "fourty-four" | +| constants.rb:2:5:4:7 | ModuleA::ClassA | A | constants.rb:79:5:79:13 | [...] | +| constants.rb:2:5:4:7 | ModuleA::ClassA | B | constants.rb:80:5:80:5 | a | +| constants.rb:2:5:4:7 | ModuleA::ClassA | C | constants.rb:81:5:81:5 | A | | constants.rb:2:5:4:7 | ModuleA::ClassA | CONST_A | constants.rb:3:19:3:27 | "const_a" | | constants.rb:2:5:4:7 | ModuleA::ClassA | GREETING | constants.rb:17:12:17:64 | ... + ... | | constants.rb:8:5:14:7 | ModuleA::ModuleB | MAX_SIZE | constants.rb:39:30:39:33 | 1024 | +| constants.rb:12:9:13:11 | ModuleA::ModuleB::ClassC | A | constants.rb:79:5:79:13 | [...] | +| constants.rb:12:9:13:11 | ModuleA::ModuleB::ClassC | B | constants.rb:80:5:80:5 | a | +| constants.rb:12:9:13:11 | ModuleA::ModuleB::ClassC | C | constants.rb:81:5:81:5 | A | | constants.rb:12:9:13:11 | ModuleA::ModuleB::ClassC | GREETING | constants.rb:17:12:17:64 | ... + ... | +| constants.rb:31:1:33:3 | ModuleA::ClassD | A | constants.rb:79:5:79:13 | [...] | +| constants.rb:31:1:33:3 | ModuleA::ClassD | B | constants.rb:80:5:80:5 | a | +| constants.rb:31:1:33:3 | ModuleA::ClassD | C | constants.rb:81:5:81:5 | A | | constants.rb:31:1:33:3 | ModuleA::ClassD | CONST_A | constants.rb:3:19:3:27 | "const_a" | | constants.rb:31:1:33:3 | ModuleA::ClassD | FOURTY_TWO | constants.rb:32:16:32:17 | 42 | | constants.rb:31:1:33:3 | ModuleA::ClassD | GREETING | constants.rb:17:12:17:64 | ... + ... | | constants.rb:35:1:37:3 | ModuleA::ModuleC | FOURTY_THREE | constants.rb:36:18:36:19 | 43 | +| constants.rb:54:3:58:5 | ModuleA::ModuleB::ClassB | A | constants.rb:79:5:79:13 | [...] | +| constants.rb:54:3:58:5 | ModuleA::ModuleB::ClassB | B | constants.rb:80:5:80:5 | a | +| constants.rb:54:3:58:5 | ModuleA::ModuleB::ClassB | C | constants.rb:81:5:81:5 | A | | constants.rb:54:3:58:5 | ModuleA::ModuleB::ClassB | FOURTY_FOUR | constants.rb:56:19:56:20 | 44 | | constants.rb:54:3:58:5 | ModuleA::ModuleB::ClassB | FOURTY_ONE | constants.rb:48:18:48:19 | 41 | | constants.rb:54:3:58:5 | ModuleA::ModuleB::ClassB | GREETING | constants.rb:17:12:17:64 | ... + ... | | constants.rb:62:3:64:5 | Mod1::Mod3 | FOURTY_FIVE | constants.rb:63:19:63:20 | 45 | | constants.rb:70:3:72:5 | Mod1::Mod3::Mod5 | FOURTY_SIX | constants.rb:71:18:71:19 | 46 | +| file://:0:0:0:0 | Object | A | constants.rb:79:5:79:13 | [...] | +| file://:0:0:0:0 | Object | B | constants.rb:80:5:80:5 | a | +| file://:0:0:0:0 | Object | C | constants.rb:81:5:81:5 | A | | file://:0:0:0:0 | Object | GREETING | constants.rb:17:12:17:64 | ... + ... | constantValue | constants.rb:17:22:17:45 | CONST_A | constants.rb:3:19:3:27 | "const_a" | @@ -108,6 +126,8 @@ constantValue | constants.rb:57:21:57:31 | FOURTY_FOUR | constants.rb:53:17:53:29 | "fourty-four" | | constants.rb:57:21:57:31 | FOURTY_FOUR | constants.rb:56:19:56:20 | 44 | | constants.rb:65:19:65:35 | FOURTY_FIVE | constants.rb:63:19:63:20 | 45 | +| constants.rb:81:5:81:5 | A | constants.rb:79:5:79:13 | [...] | +| constants.rb:82:5:82:5 | B | constants.rb:80:5:80:5 | a | constantWriteAccessQualifiedName | constants.rb:1:1:15:3 | ModuleA | ModuleA | | constants.rb:2:5:4:7 | ClassA | ModuleA::ClassA | @@ -149,3 +169,5 @@ arrayConstant | constants.rb:79:5:79:13 | call to [] | constants.rb:79:5:79:13 | call to [] | | constants.rb:80:5:80:5 | a | constants.rb:78:5:78:13 | call to [] | | constants.rb:81:5:81:5 | A | constants.rb:79:5:79:13 | call to [] | +| constants.rb:82:5:82:5 | B | constants.rb:78:5:78:13 | call to [] | +| constants.rb:85:7:85:7 | b | constants.rb:78:5:78:13 | call to [] | diff --git a/ruby/ql/test/library-tests/ast/constants/constants.rb b/ruby/ql/test/library-tests/ast/constants/constants.rb index fce55bccd00..359a861eb1e 100644 --- a/ruby/ql/test/library-tests/ast/constants/constants.rb +++ b/ruby/ql/test/library-tests/ast/constants/constants.rb @@ -79,4 +79,9 @@ a = [1, 2, 3] A = [1, 2, 3] B = a C = A -b = B \ No newline at end of file +b = B + +if condition + c = b +end +c # not recognised From 4cfaa86d5d4329d32455fd0c83aba6f80de1f066 Mon Sep 17 00:00:00 2001 From: Harry Maclean Date: Wed, 13 Jul 2022 12:39:47 +1200 Subject: [PATCH 216/505] Ruby: Update new-style barrier-guard --- ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll b/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll index 52e9c48779d..b86f976d119 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll @@ -84,13 +84,16 @@ deprecated class StringConstCompare extends DataFlow::BarrierGuard, } private predicate stringConstArrayInclusionCall(CfgNodes::ExprCfgNode g, CfgNode e, boolean branch) { - exists(CfgNodes::ExprNodes::MethodCallCfgNode mc, ArrayLiteral aLit | + exists(CfgNodes::ExprNodes::MethodCallCfgNode mc | mc = g and mc.getExpr().getMethodName() = "include?" and - [mc.getExpr().getReceiver(), mc.getExpr().getReceiver().(ConstantReadAccess).getValue()] = aLit - | - forall(Expr elem | elem = aLit.getAnElement() | elem instanceof StringLiteral) and mc.getArgument(0) = e + | + exists(ExprNodes::ArrayLiteralCfgNode arr | isArrayConstant(mc.getReceiver(), arr) | + forall(ExprCfgNode elem | elem = arr.getAnArgument() | + elem instanceof ExprNodes::StringLiteralCfgNode + ) + ) ) and branch = true } From b9fc82a741c74b2b812cfe9a2e1ae8ae47f14931 Mon Sep 17 00:00:00 2001 From: Harry Maclean Date: Wed, 13 Jul 2022 12:56:03 +1200 Subject: [PATCH 217/505] Ruby: Test both old and new-style barrier guards --- .../codeql/ruby/dataflow/BarrierGuards.qll | 24 ++----------------- .../barrier-guards/barrier-guards.expected | 11 +++++++++ .../dataflow/barrier-guards/barrier-guards.ql | 16 +++++++++++++ 3 files changed, 29 insertions(+), 22 deletions(-) create mode 100644 ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.ql diff --git a/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll b/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll index b86f976d119..e32eb11fa1a 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll @@ -64,19 +64,7 @@ deprecated class StringConstCompare extends DataFlow::BarrierGuard, // The value of the condition that results in the node being validated. private boolean checkedBranch; - StringConstCompare() { - exists(CfgNodes::ExprNodes::StringLiteralCfgNode strLitNode | - this.getExpr() instanceof EqExpr and checkedBranch = true - or - this.getExpr() instanceof CaseEqExpr and checkedBranch = true - or - this.getExpr() instanceof NEExpr and checkedBranch = false - | - this.getLeftOperand() = strLitNode and this.getRightOperand() = checkedNode - or - this.getLeftOperand() = checkedNode and this.getRightOperand() = strLitNode - ) - } + StringConstCompare() { stringConstCompare(this, checkedNode, checkedBranch) } override predicate checks(CfgNode expr, boolean branch) { expr = checkedNode and branch = checkedBranch @@ -138,15 +126,7 @@ deprecated class StringConstArrayInclusionCall extends DataFlow::BarrierGuard, CfgNodes::ExprNodes::MethodCallCfgNode { private CfgNode checkedNode; - StringConstArrayInclusionCall() { - this.getMethodName() = "include?" and - this.getArgument(0) = checkedNode and - exists(ExprNodes::ArrayLiteralCfgNode arr | isArrayConstant(this.getReceiver(), arr) | - forall(ExprCfgNode elem | elem = arr.getAnArgument() | - elem instanceof ExprNodes::StringLiteralCfgNode - ) - ) - } + StringConstArrayInclusionCall() { stringConstArrayInclusionCall(this, checkedNode, _) } override predicate checks(CfgNode expr, boolean branch) { expr = checkedNode and branch = true } } diff --git a/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.expected b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.expected index fcf95346a5a..ea99ed7d669 100644 --- a/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.expected +++ b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.expected @@ -1,3 +1,5 @@ +WARNING: Type BarrierGuard has been deprecated and may be removed in future (barrier-guards.ql:8,3-15) +oldStyleBarrierGuards | barrier-guards.rb:3:4:3:15 | ... == ... | barrier-guards.rb:4:5:4:7 | foo | barrier-guards.rb:3:4:3:6 | foo | true | | barrier-guards.rb:9:4:9:24 | call to include? | barrier-guards.rb:10:5:10:7 | foo | barrier-guards.rb:9:21:9:23 | foo | true | | barrier-guards.rb:15:4:15:15 | ... != ... | barrier-guards.rb:18:5:18:7 | foo | barrier-guards.rb:15:4:15:6 | foo | false | @@ -6,3 +8,12 @@ | barrier-guards.rb:37:4:37:20 | call to include? | barrier-guards.rb:38:5:38:7 | foo | barrier-guards.rb:37:17:37:19 | foo | true | | barrier-guards.rb:43:4:43:15 | ... == ... | barrier-guards.rb:45:9:45:11 | foo | barrier-guards.rb:43:4:43:6 | foo | true | | barrier-guards.rb:70:4:70:21 | call to include? | barrier-guards.rb:71:5:71:7 | foo | barrier-guards.rb:70:18:70:20 | foo | true | +newStyleBarrierGuards +| barrier-guards.rb:4:5:4:7 | foo | +| barrier-guards.rb:10:5:10:7 | foo | +| barrier-guards.rb:18:5:18:7 | foo | +| barrier-guards.rb:24:5:24:7 | foo | +| barrier-guards.rb:28:5:28:7 | foo | +| barrier-guards.rb:38:5:38:7 | foo | +| barrier-guards.rb:45:9:45:11 | foo | +| barrier-guards.rb:71:5:71:7 | foo | diff --git a/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.ql b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.ql new file mode 100644 index 00000000000..84a962ade35 --- /dev/null +++ b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.ql @@ -0,0 +1,16 @@ +import codeql.ruby.dataflow.internal.DataFlowPublic +import codeql.ruby.dataflow.BarrierGuards +import codeql.ruby.controlflow.CfgNodes +import codeql.ruby.controlflow.ControlFlowGraph +import codeql.ruby.DataFlow + +query predicate oldStyleBarrierGuards( + BarrierGuard g, DataFlow::Node guardedNode, ExprCfgNode expr, boolean branch +) { + g.checks(expr, branch) and guardedNode = g.getAGuardedNode() +} + +query predicate newStyleBarrierGuards(DataFlow::Node n) { + n instanceof StringConstCompareBarrier or + n instanceof StringConstArrayInclusionCallBarrier +} From ea95e2e1d0a65ba680b2cb3f2b8796c2633aef59 Mon Sep 17 00:00:00 2001 From: Harry Maclean Date: Wed, 13 Jul 2022 18:01:06 +1200 Subject: [PATCH 218/505] Ruby: Use InclusionTests library in barrier guards --- .../lib/codeql/ruby/dataflow/BarrierGuards.qll | 18 ++++++++++-------- .../barrier-guards/barrier-guards.expected | 3 +++ .../dataflow/barrier-guards/barrier-guards.rb | 12 ++++++++++++ 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll b/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll index e32eb11fa1a..c79e4311489 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll @@ -6,6 +6,7 @@ private import codeql.ruby.CFG private import codeql.ruby.controlflow.CfgNodes private import codeql.ruby.dataflow.SSA private import codeql.ruby.ast.internal.Constant +private import codeql.ruby.InclusionTests private predicate stringConstCompare(CfgNodes::ExprCfgNode g, CfgNode e, boolean branch) { exists(CfgNodes::ExprNodes::ComparisonOperationCfgNode c | @@ -72,18 +73,19 @@ deprecated class StringConstCompare extends DataFlow::BarrierGuard, } private predicate stringConstArrayInclusionCall(CfgNodes::ExprCfgNode g, CfgNode e, boolean branch) { - exists(CfgNodes::ExprNodes::MethodCallCfgNode mc | - mc = g and - mc.getExpr().getMethodName() = "include?" and - mc.getArgument(0) = e + exists(InclusionTest t | + t.asExpr() = g and + e = t.getContainedNode().asExpr() and + branch = t.getPolarity() | - exists(ExprNodes::ArrayLiteralCfgNode arr | isArrayConstant(mc.getReceiver(), arr) | + exists(ExprNodes::ArrayLiteralCfgNode arr | + isArrayConstant(t.getContainerNode().asExpr(), arr) + | forall(ExprCfgNode elem | elem = arr.getAnArgument() | elem instanceof ExprNodes::StringLiteralCfgNode ) ) - ) and - branch = true + ) } /** @@ -126,7 +128,7 @@ deprecated class StringConstArrayInclusionCall extends DataFlow::BarrierGuard, CfgNodes::ExprNodes::MethodCallCfgNode { private CfgNode checkedNode; - StringConstArrayInclusionCall() { stringConstArrayInclusionCall(this, checkedNode, _) } + StringConstArrayInclusionCall() { stringConstArrayInclusionCall(this, checkedNode, true) } override predicate checks(CfgNode expr, boolean branch) { expr = checkedNode and branch = true } } diff --git a/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.expected b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.expected index ea99ed7d669..783d414dad6 100644 --- a/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.expected +++ b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.expected @@ -8,6 +8,7 @@ oldStyleBarrierGuards | barrier-guards.rb:37:4:37:20 | call to include? | barrier-guards.rb:38:5:38:7 | foo | barrier-guards.rb:37:17:37:19 | foo | true | | barrier-guards.rb:43:4:43:15 | ... == ... | barrier-guards.rb:45:9:45:11 | foo | barrier-guards.rb:43:4:43:6 | foo | true | | barrier-guards.rb:70:4:70:21 | call to include? | barrier-guards.rb:71:5:71:7 | foo | barrier-guards.rb:70:18:70:20 | foo | true | +| barrier-guards.rb:82:4:82:25 | ... != ... | barrier-guards.rb:83:5:83:7 | foo | barrier-guards.rb:82:15:82:17 | foo | true | newStyleBarrierGuards | barrier-guards.rb:4:5:4:7 | foo | | barrier-guards.rb:10:5:10:7 | foo | @@ -17,3 +18,5 @@ newStyleBarrierGuards | barrier-guards.rb:38:5:38:7 | foo | | barrier-guards.rb:45:9:45:11 | foo | | barrier-guards.rb:71:5:71:7 | foo | +| barrier-guards.rb:83:5:83:7 | foo | +| barrier-guards.rb:91:5:91:7 | foo | diff --git a/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.rb b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.rb index a3f13b48639..47b96da22dd 100644 --- a/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.rb +++ b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.rb @@ -79,6 +79,18 @@ else foo end +if foos.index(foo) != nil + foo +else + foo +end + +if foos.index(foo)r == nil + foo +else + foo +end + bars = ["bar"] if condition From 49aab5189362f85d6b0c320b6b031f14eb80847d Mon Sep 17 00:00:00 2001 From: Harry Maclean Date: Wed, 13 Jul 2022 18:02:33 +1200 Subject: [PATCH 219/505] Ruby: Make helper predicate private --- ruby/ql/lib/codeql/ruby/ast/internal/Constant.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruby/ql/lib/codeql/ruby/ast/internal/Constant.qll b/ruby/ql/lib/codeql/ruby/ast/internal/Constant.qll index e7e303a9fa6..53355695e57 100644 --- a/ruby/ql/lib/codeql/ruby/ast/internal/Constant.qll +++ b/ruby/ql/lib/codeql/ruby/ast/internal/Constant.qll @@ -533,7 +533,7 @@ predicate isArrayConstant(ExprCfgNode e, ArrayLiteralCfgNode arr) { /** * Holds if the expression `e` refers to an array constructed from the array literal `arr`. */ -predicate isArrayExpr(Expr e, ArrayLiteralCfgNode arr) { +private predicate isArrayExpr(Expr e, ArrayLiteralCfgNode arr) { // e = [...] e = arr.getExpr() or From 2850b35a04cc7c457e63369dbf9932094c36ecb5 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 25 May 2022 14:07:38 +0200 Subject: [PATCH 220/505] update, and fix, the autobuilders by using the new --also-match option --- ql/autobuilder/src/main.rs | 30 ++++++++++++++++++++---------- ruby/autobuilder/src/main.rs | 18 ++++++++++++++++-- 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/ql/autobuilder/src/main.rs b/ql/autobuilder/src/main.rs index 45977b3792d..4bb03eb0ed4 100644 --- a/ql/autobuilder/src/main.rs +++ b/ql/autobuilder/src/main.rs @@ -15,29 +15,39 @@ fn main() -> std::io::Result<()> { let mut cmd = Command::new(codeql); cmd.arg("database") .arg("index-files") + .arg("--include-extension=.ql") + .arg("--include-extension=.qll") + .arg("--include-extension=.dbscheme") + .arg("--include=**/qlpack.yml") .arg("--size-limit=5m") .arg("--language=ql") .arg("--working-dir=.") .arg(db); - let mut has_include_dir = false; // TODO: This is a horrible hack, wait for the post-merge discussion in https://github.com/github/codeql/pull/7444 to be resolved + let pwd = env::current_dir()?; for line in env::var("LGTM_INDEX_FILTERS") .unwrap_or_default() .split('\n') { if let Some(stripped) = line.strip_prefix("include:") { - cmd.arg("--include").arg(stripped); - has_include_dir = true; + let path = pwd + .join(stripped) + .join("**") + .into_os_string() + .into_string() + .unwrap(); + cmd.arg("--also-match").arg(path); } else if let Some(stripped) = line.strip_prefix("exclude:") { - cmd.arg("--exclude").arg(stripped); + let path = pwd + .join(stripped) + .join("**") + .into_os_string() + .into_string() + .unwrap(); + // the same as above, but starting with "!" + cmd.arg("--also-match").arg("!".to_owned() + &path); } } - if !has_include_dir { - cmd.arg("--include-extension=.ql") - .arg("--include-extension=.qll") - .arg("--include-extension=.dbscheme") - .arg("--include=**/qlpack.yml"); - } let exit = &cmd.spawn()?.wait()?; std::process::exit(exit.code().unwrap_or(1)) } diff --git a/ruby/autobuilder/src/main.rs b/ruby/autobuilder/src/main.rs index 18892ae2d5c..6573f4cfd33 100644 --- a/ruby/autobuilder/src/main.rs +++ b/ruby/autobuilder/src/main.rs @@ -24,14 +24,28 @@ fn main() -> std::io::Result<()> { .arg("--working-dir=.") .arg(db); + let pwd = env::current_dir()?; for line in env::var("LGTM_INDEX_FILTERS") .unwrap_or_default() .split('\n') { if let Some(stripped) = line.strip_prefix("include:") { - cmd.arg("--include").arg(stripped); + let path = pwd + .join(stripped) + .join("**") + .into_os_string() + .into_string() + .unwrap(); + cmd.arg("--also-match").arg(path); } else if let Some(stripped) = line.strip_prefix("exclude:") { - cmd.arg("--exclude").arg(stripped); + let path = pwd + .join(stripped) + .join("**") + .into_os_string() + .into_string() + .unwrap(); + // the same as above, but starting with "!" + cmd.arg("--also-match").arg("!".to_owned() + &path); } } let exit = &cmd.spawn()?.wait()?; From 878168384ee82c1a00d28bb4c352c6a9a371658d Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 25 May 2022 14:07:59 +0200 Subject: [PATCH 221/505] remove tools:latest from codeql-action in QL-for-QL --- .github/workflows/ql-for-ql-build.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/ql-for-ql-build.yml b/.github/workflows/ql-for-ql-build.yml index 95b93dd772c..2d96fbfa797 100644 --- a/.github/workflows/ql-for-ql-build.yml +++ b/.github/workflows/ql-for-ql-build.yml @@ -19,7 +19,6 @@ jobs: uses: github/codeql-action/init@aa93aea877e5fb8841bcb1193f672abf6e9f2980 with: languages: javascript # does not matter - tools: latest - name: Get CodeQL version id: get-codeql-version run: | @@ -184,7 +183,6 @@ jobs: languages: ql db-location: ${{ runner.temp }}/db config-file: ./ql-for-ql-config.yml - tools: latest - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@aa93aea877e5fb8841bcb1193f672abf6e9f2980 @@ -224,4 +222,4 @@ jobs: uses: actions/upload-artifact@v3 with: name: combined.sarif - path: combined.sarif + path: combined.sarif \ No newline at end of file From eb0340dcb6ba5154f55335e9963961e6e9becf3a Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 30 May 2022 22:18:00 +0200 Subject: [PATCH 222/505] get excludes to work properly --- ql/autobuilder/src/main.rs | 25 +++++++++---------------- ruby/autobuilder/src/main.rs | 25 +++++++++---------------- 2 files changed, 18 insertions(+), 32 deletions(-) diff --git a/ql/autobuilder/src/main.rs b/ql/autobuilder/src/main.rs index 4bb03eb0ed4..7cc4900a6b7 100644 --- a/ql/autobuilder/src/main.rs +++ b/ql/autobuilder/src/main.rs @@ -24,30 +24,23 @@ fn main() -> std::io::Result<()> { .arg("--working-dir=.") .arg(db); - let pwd = env::current_dir()?; for line in env::var("LGTM_INDEX_FILTERS") .unwrap_or_default() .split('\n') { if let Some(stripped) = line.strip_prefix("include:") { - let path = pwd - .join(stripped) - .join("**") - .into_os_string() - .into_string() - .unwrap(); - cmd.arg("--also-match").arg(path); + cmd.arg("--also-match").arg(absolutelyfy(stripped)); } else if let Some(stripped) = line.strip_prefix("exclude:") { - let path = pwd - .join(stripped) - .join("**") - .into_os_string() - .into_string() - .unwrap(); - // the same as above, but starting with "!" - cmd.arg("--also-match").arg("!".to_owned() + &path); + cmd.arg("--exclude").arg(stripped); } } let exit = &cmd.spawn()?.wait()?; std::process::exit(exit.code().unwrap_or(1)) } + +// converts the relative path `stripped` to an absolute path by prepending the working directory +fn absolutelyfy(stripped: &str) -> String { + let pwd = env::current_dir().unwrap(); + + pwd.join(stripped).into_os_string().into_string().unwrap() +} diff --git a/ruby/autobuilder/src/main.rs b/ruby/autobuilder/src/main.rs index 6573f4cfd33..496c34a0594 100644 --- a/ruby/autobuilder/src/main.rs +++ b/ruby/autobuilder/src/main.rs @@ -24,30 +24,23 @@ fn main() -> std::io::Result<()> { .arg("--working-dir=.") .arg(db); - let pwd = env::current_dir()?; for line in env::var("LGTM_INDEX_FILTERS") .unwrap_or_default() .split('\n') { if let Some(stripped) = line.strip_prefix("include:") { - let path = pwd - .join(stripped) - .join("**") - .into_os_string() - .into_string() - .unwrap(); - cmd.arg("--also-match").arg(path); + cmd.arg("--also-match").arg(absolutelyfy(stripped)); } else if let Some(stripped) = line.strip_prefix("exclude:") { - let path = pwd - .join(stripped) - .join("**") - .into_os_string() - .into_string() - .unwrap(); - // the same as above, but starting with "!" - cmd.arg("--also-match").arg("!".to_owned() + &path); + cmd.arg("--exclude").arg(stripped); } } let exit = &cmd.spawn()?.wait()?; std::process::exit(exit.code().unwrap_or(1)) } + +// converts the relative path `stripped` to an absolute path by prepending the working directory +fn absolutelyfy(stripped: &str) -> String { + let pwd = env::current_dir().unwrap(); + + pwd.join(stripped).into_os_string().into_string().unwrap() +} From 047b14e310bef5eee5ce07d18b3f6dca01de38da Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 31 May 2022 10:25:48 +0000 Subject: [PATCH 223/505] get the autobuilders to work after introducing test-cases --- ql/autobuilder/src/main.rs | 11 ++--------- ruby/autobuilder/src/main.rs | 11 ++--------- 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/ql/autobuilder/src/main.rs b/ql/autobuilder/src/main.rs index 7cc4900a6b7..df47cc33184 100644 --- a/ql/autobuilder/src/main.rs +++ b/ql/autobuilder/src/main.rs @@ -29,18 +29,11 @@ fn main() -> std::io::Result<()> { .split('\n') { if let Some(stripped) = line.strip_prefix("include:") { - cmd.arg("--also-match").arg(absolutelyfy(stripped)); + cmd.arg("--also-match=".to_owned() + stripped); } else if let Some(stripped) = line.strip_prefix("exclude:") { - cmd.arg("--exclude").arg(stripped); + cmd.arg("--exclude=".to_owned() + stripped); } } let exit = &cmd.spawn()?.wait()?; std::process::exit(exit.code().unwrap_or(1)) } - -// converts the relative path `stripped` to an absolute path by prepending the working directory -fn absolutelyfy(stripped: &str) -> String { - let pwd = env::current_dir().unwrap(); - - pwd.join(stripped).into_os_string().into_string().unwrap() -} diff --git a/ruby/autobuilder/src/main.rs b/ruby/autobuilder/src/main.rs index 496c34a0594..8f0f1b48d0d 100644 --- a/ruby/autobuilder/src/main.rs +++ b/ruby/autobuilder/src/main.rs @@ -29,18 +29,11 @@ fn main() -> std::io::Result<()> { .split('\n') { if let Some(stripped) = line.strip_prefix("include:") { - cmd.arg("--also-match").arg(absolutelyfy(stripped)); + cmd.arg("--also-match=".to_owned() + stripped); } else if let Some(stripped) = line.strip_prefix("exclude:") { - cmd.arg("--exclude").arg(stripped); + cmd.arg("--exclude=".to_owned() + stripped); } } let exit = &cmd.spawn()?.wait()?; std::process::exit(exit.code().unwrap_or(1)) } - -// converts the relative path `stripped` to an absolute path by prepending the working directory -fn absolutelyfy(stripped: &str) -> String { - let pwd = env::current_dir().unwrap(); - - pwd.join(stripped).into_os_string().into_string().unwrap() -} From dded3af3d8ccead9e640652ce18017bd45b16025 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 13 Jul 2022 09:57:17 +0200 Subject: [PATCH 224/505] remove more false positives from the ql/missing-parameter-qldoc query --- ql/ql/src/queries/style/MissingParameterInQlDoc.ql | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ql/ql/src/queries/style/MissingParameterInQlDoc.ql b/ql/ql/src/queries/style/MissingParameterInQlDoc.ql index b378803822a..c2b1e80fe53 100644 --- a/ql/ql/src/queries/style/MissingParameterInQlDoc.ql +++ b/ql/ql/src/queries/style/MissingParameterInQlDoc.ql @@ -44,7 +44,10 @@ private string getAMentionedNonParameter(Predicate p) { not result.toLowerCase() = getAParameterName(p).toLowerCase() and // keywords not result = - ["true", "false", "NaN", "this", "forall", "exists", "null", "break", "return", "not"] and + [ + "true", "false", "NaN", "this", "forall", "exists", "null", "break", "return", "not", "if", + "then", "else", "import" + ] and not result = any(Aggregate a).getKind() and // min, max, sum, count, etc. not result = getMentionedThings(p.getLocation().getFile()) and not result = any(Annotation a).getName() and // private, final, etc. @@ -66,7 +69,7 @@ private string getMentionedThings(File file) { private string getAnUndocumentedParameter(Predicate p) { result = getAParameterName(p) and not result.toLowerCase() = getADocumentedParameter(p).toLowerCase() and - not result = ["config", "conf", "cfg"] and // DataFlow configurations are often undocumented, and that's fine. + not result = ["config", "conf", "cfg", "t", "t2"] and // DataFlow configurations / type-trackers are often undocumented, and that's fine. not ( // "the given" often refers to the first parameter. p.getQLDoc().getContents().regexpMatch("(?s).*\\bthe given\\b.*") and @@ -85,7 +88,7 @@ private string getMentionedNonParameters(Predicate p) { } from Predicate p -where not p.getLocation().getFile().getBaseName() = "Aliases.qll" // these are OK +where not p.getLocation().getFile().getBaseName() in ["Aliases.qll", "TreeSitter.qll"] // these are OK select p, "The QLDoc has no documentation for " + getUndocumentedParameters(p) + ", but the QLDoc mentions " + getMentionedNonParameters(p) From c4f44bb67fbd989c61610011a12af863efd50b1b Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 13 Jul 2022 10:01:26 +0200 Subject: [PATCH 225/505] sync files --- python/ql/src/Security/CWE-020/HostnameRegexpShared.qll | 2 +- ruby/ql/src/queries/security/cwe-020/HostnameRegexpShared.qll | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/python/ql/src/Security/CWE-020/HostnameRegexpShared.qll b/python/ql/src/Security/CWE-020/HostnameRegexpShared.qll index 2192951f76d..5e9bc406512 100644 --- a/python/ql/src/Security/CWE-020/HostnameRegexpShared.qll +++ b/python/ql/src/Security/CWE-020/HostnameRegexpShared.qll @@ -53,7 +53,7 @@ predicate matchesBeginningOfString(RegExpTerm term) { } /** - * Holds if the given sequence contains top-level domain preceded by a dot, such as `.com`, + * Holds if the given sequence `seq` contains top-level domain preceded by a dot, such as `.com`, * excluding cases where this is at the very beginning of the regexp. * * `i` is bound to the index of the last child in the top-level domain part. diff --git a/ruby/ql/src/queries/security/cwe-020/HostnameRegexpShared.qll b/ruby/ql/src/queries/security/cwe-020/HostnameRegexpShared.qll index 2192951f76d..5e9bc406512 100644 --- a/ruby/ql/src/queries/security/cwe-020/HostnameRegexpShared.qll +++ b/ruby/ql/src/queries/security/cwe-020/HostnameRegexpShared.qll @@ -53,7 +53,7 @@ predicate matchesBeginningOfString(RegExpTerm term) { } /** - * Holds if the given sequence contains top-level domain preceded by a dot, such as `.com`, + * Holds if the given sequence `seq` contains top-level domain preceded by a dot, such as `.com`, * excluding cases where this is at the very beginning of the regexp. * * `i` is bound to the index of the last child in the top-level domain part. From cd5fbe633f2206b264ebcfb7ea25eca5b08f915c Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 13 Jul 2022 10:12:52 +0200 Subject: [PATCH 226/505] update locations in test after merging in the focus-location-pr --- .../MissingParameterInQlDoc/MissingParameterInQlDoc.expected | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ql/ql/test/queries/style/MissingParameterInQlDoc/MissingParameterInQlDoc.expected b/ql/ql/test/queries/style/MissingParameterInQlDoc/MissingParameterInQlDoc.expected index cca5b457ec4..4307178da72 100644 --- a/ql/ql/test/queries/style/MissingParameterInQlDoc/MissingParameterInQlDoc.expected +++ b/ql/ql/test/queries/style/MissingParameterInQlDoc/MissingParameterInQlDoc.expected @@ -1,2 +1,2 @@ -| Foo.qll:5:1:5:50 | ClasslessPredicate test2 | The QLDoc has no documentation for param2, but the QLDoc mentions par2 | -| Foo.qll:14:1:14:50 | ClasslessPredicate test5 | The QLDoc has no documentation for param2, but the QLDoc mentions par2 | +| Foo.qll:5:11:5:15 | ClasslessPredicate test2 | The QLDoc has no documentation for param2, but the QLDoc mentions par2 | +| Foo.qll:14:11:14:15 | ClasslessPredicate test5 | The QLDoc has no documentation for param2, but the QLDoc mentions par2 | From fd10947ca0f1f17d42436a7173c734f2d75cd37d Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 11 Jul 2022 16:22:54 +0200 Subject: [PATCH 227/505] use small steps in TypeBackTracker correctly --- .../lib/semmle/javascript/dataflow/TypeTracking.qll | 2 +- .../UnsafeHtmlConstructionCustomizations.qll | 2 +- .../src/Security/CWE-094/ImproperCodeSanitization.ql | 2 +- .../UnsafeHtmlConstruction.expected | 12 ++++++++++++ .../Security/CWE-079/UnsafeHtmlConstruction/main.js | 8 ++++++++ 5 files changed, 23 insertions(+), 3 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/dataflow/TypeTracking.qll b/javascript/ql/lib/semmle/javascript/dataflow/TypeTracking.qll index 896f0177ec3..12aa8d09ed1 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/TypeTracking.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/TypeTracking.qll @@ -312,7 +312,7 @@ class TypeBackTracker extends TTypeBackTracker { * result = < some API call >.getArgument(< n >) * or * exists (DataFlow::TypeBackTracker t2 | - * t = t2.smallstep(result, myType(t2)) + * t2 = t.smallstep(result, myType(t2)) * ) * } * diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll index d594da271b8..fd549429e4a 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll @@ -80,7 +80,7 @@ module UnsafeHtmlConstruction { t.start() and result = sink or - exists(DataFlow::TypeBackTracker t2 | t = t2.smallstep(result, isUsedInXssSink(t2, sink))) + exists(DataFlow::TypeBackTracker t2 | t2 = t.smallstep(result, isUsedInXssSink(t2, sink))) or exists(DataFlow::TypeBackTracker t2 | t.continue() = t2 and diff --git a/javascript/ql/src/Security/CWE-094/ImproperCodeSanitization.ql b/javascript/ql/src/Security/CWE-094/ImproperCodeSanitization.ql index 886c78b0161..694e827ba41 100644 --- a/javascript/ql/src/Security/CWE-094/ImproperCodeSanitization.ql +++ b/javascript/ql/src/Security/CWE-094/ImproperCodeSanitization.ql @@ -50,7 +50,7 @@ private DataFlow::Node endsInCodeInjectionSink(DataFlow::TypeBackTracker t) { not result instanceof StringOps::ConcatenationRoot // the heuristic CodeInjection sink looks for string-concats, we are not interrested in those here. ) or - exists(DataFlow::TypeBackTracker t2 | t = t2.smallstep(result, endsInCodeInjectionSink(t2))) + exists(DataFlow::TypeBackTracker t2 | t2 = t.smallstep(result, endsInCodeInjectionSink(t2))) } /** diff --git a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/UnsafeHtmlConstruction.expected b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/UnsafeHtmlConstruction.expected index c604f4ab2d2..94f1fe314b0 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/UnsafeHtmlConstruction.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/UnsafeHtmlConstruction.expected @@ -50,6 +50,12 @@ nodes | main.js:79:34:79:36 | val | | main.js:81:35:81:37 | val | | main.js:81:35:81:37 | val | +| main.js:89:21:89:21 | x | +| main.js:90:23:90:23 | x | +| main.js:90:23:90:23 | x | +| main.js:93:43:93:43 | x | +| main.js:93:43:93:43 | x | +| main.js:94:31:94:31 | x | | typed.ts:1:39:1:39 | s | | typed.ts:1:39:1:39 | s | | typed.ts:2:29:2:29 | s | @@ -115,6 +121,11 @@ edges | main.js:79:34:79:36 | val | main.js:81:35:81:37 | val | | main.js:79:34:79:36 | val | main.js:81:35:81:37 | val | | main.js:79:34:79:36 | val | main.js:81:35:81:37 | val | +| main.js:89:21:89:21 | x | main.js:90:23:90:23 | x | +| main.js:89:21:89:21 | x | main.js:90:23:90:23 | x | +| main.js:93:43:93:43 | x | main.js:94:31:94:31 | x | +| main.js:93:43:93:43 | x | main.js:94:31:94:31 | x | +| main.js:94:31:94:31 | x | main.js:89:21:89:21 | x | | typed.ts:1:39:1:39 | s | typed.ts:2:29:2:29 | s | | typed.ts:1:39:1:39 | s | typed.ts:2:29:2:29 | s | | typed.ts:1:39:1:39 | s | typed.ts:2:29:2:29 | s | @@ -141,5 +152,6 @@ edges | main.js:62:19:62:31 | settings.name | main.js:56:28:56:34 | options | main.js:62:19:62:31 | settings.name | $@ based on $@ might later cause $@. | main.js:62:19:62:31 | settings.name | HTML construction | main.js:56:28:56:34 | options | library input | main.js:62:11:62:40 | "" + ... "" | cross-site scripting | | main.js:67:63:67:69 | attrVal | main.js:66:35:66:41 | attrVal | main.js:67:63:67:69 | attrVal | $@ based on $@ might later cause $@. | main.js:67:63:67:69 | attrVal | HTML construction | main.js:66:35:66:41 | attrVal | library input | main.js:67:47:67:78 | "" | cross-site scripting | | main.js:81:35:81:37 | val | main.js:79:34:79:36 | val | main.js:81:35:81:37 | val | $@ based on $@ might later cause $@. | main.js:81:35:81:37 | val | HTML construction | main.js:79:34:79:36 | val | library input | main.js:81:24:81:49 | " ... /span>" | cross-site scripting | +| main.js:90:23:90:23 | x | main.js:93:43:93:43 | x | main.js:90:23:90:23 | x | $@ based on $@ might later cause $@. | main.js:90:23:90:23 | x | HTML construction | main.js:93:43:93:43 | x | library input | main.js:94:20:94:32 | createHTML(x) | cross-site scripting | | typed.ts:2:29:2:29 | s | typed.ts:1:39:1:39 | s | typed.ts:2:29:2:29 | s | $@ based on $@ might later cause $@. | typed.ts:2:29:2:29 | s | HTML construction | typed.ts:1:39:1:39 | s | library input | typed.ts:3:31:3:34 | html | cross-site scripting | | typed.ts:8:40:8:40 | s | typed.ts:6:43:6:43 | s | typed.ts:8:40:8:40 | s | $@ based on $@ might later cause $@. | typed.ts:8:40:8:40 | s | HTML construction | typed.ts:6:43:6:43 | s | library input | typed.ts:8:29:8:52 | " ... /span>" | cross-site scripting | diff --git a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/main.js b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/main.js index 1547ae86b24..2e9d344b1f3 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/main.js +++ b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/main.js @@ -85,3 +85,11 @@ module.exports.types = function (val) { $("#foo").html("" + val + ""); // OK } } + +function createHTML(x) { + return "" + x + ""; // NOT OK +} + +module.exports.usesCreateHTML = function (x) { + $("#foo").html(createHTML(x)); +} \ No newline at end of file From 1fa214471659a6769b5ab7ba61fb6555fe9846f8 Mon Sep 17 00:00:00 2001 From: Harry Maclean Date: Wed, 13 Jul 2022 21:02:08 +1200 Subject: [PATCH 228/505] Ruby: Update test fixtures --- ruby/ql/test/library-tests/ast/Ast.expected | 29 ++++++++++++ .../library-tests/ast/AstDesugar.expected | 12 +++++ .../library-tests/ast/TreeSitter.expected | 46 +++++++++++++++++++ .../test/library-tests/ast/ValueText.expected | 12 +++++ 4 files changed, 99 insertions(+) diff --git a/ruby/ql/test/library-tests/ast/Ast.expected b/ruby/ql/test/library-tests/ast/Ast.expected index ebfd53fd551..0a16654594d 100644 --- a/ruby/ql/test/library-tests/ast/Ast.expected +++ b/ruby/ql/test/library-tests/ast/Ast.expected @@ -1556,6 +1556,35 @@ constants/constants.rb: # 73| getAnOperand/getLeftOperand: [ClassVariableAccess] @@fourty_six # 73| getAnOperand/getRightOperand: [ConstantReadAccess] FOURTY_SIX # 73| getScopeExpr: [ConstantReadAccess] Mod3 +# 78| getStmt: [AssignExpr] ... = ... +# 78| getAnOperand/getLeftOperand: [LocalVariableAccess] a +# 78| getAnOperand/getRightOperand: [ArrayLiteral] [...] +# 78| getElement: [IntegerLiteral] 1 +# 78| getElement: [IntegerLiteral] 2 +# 78| getElement: [IntegerLiteral] 3 +# 79| getStmt: [AssignExpr] ... = ... +# 79| getAnOperand/getLeftOperand: [ConstantAssignment] A +# 79| getAnOperand/getRightOperand: [ArrayLiteral] [...] +# 79| getElement: [IntegerLiteral] 1 +# 79| getElement: [IntegerLiteral] 2 +# 79| getElement: [IntegerLiteral] 3 +# 80| getStmt: [AssignExpr] ... = ... +# 80| getAnOperand/getLeftOperand: [ConstantAssignment] B +# 80| getAnOperand/getRightOperand: [LocalVariableAccess] a +# 81| getStmt: [AssignExpr] ... = ... +# 81| getAnOperand/getLeftOperand: [ConstantAssignment] C +# 81| getAnOperand/getRightOperand: [ConstantReadAccess] A +# 82| getStmt: [AssignExpr] ... = ... +# 82| getAnOperand/getLeftOperand: [LocalVariableAccess] b +# 82| getAnOperand/getRightOperand: [ConstantReadAccess] B +# 84| getStmt: [IfExpr] if ... +# 84| getCondition: [MethodCall] call to condition +# 84| getReceiver: [SelfVariableAccess] self +# 84| getBranch/getThen: [StmtSequence] then ... +# 85| getStmt: [AssignExpr] ... = ... +# 85| getAnOperand/getLeftOperand: [LocalVariableAccess] c +# 85| getAnOperand/getRightOperand: [LocalVariableAccess] b +# 87| getStmt: [LocalVariableAccess] c escape_sequences/escapes.rb: # 1| [Toplevel] escapes.rb # 6| getStmt: [StringLiteral] "\'" diff --git a/ruby/ql/test/library-tests/ast/AstDesugar.expected b/ruby/ql/test/library-tests/ast/AstDesugar.expected index 0e6a0694105..956893e944f 100644 --- a/ruby/ql/test/library-tests/ast/AstDesugar.expected +++ b/ruby/ql/test/library-tests/ast/AstDesugar.expected @@ -336,6 +336,18 @@ constants/constants.rb: # 20| getComponent: [StringTextComponent] Chuck # 20| getArgument: [StringLiteral] "Dave" # 20| getComponent: [StringTextComponent] Dave +# 78| [ArrayLiteral] [...] +# 78| getDesugared: [MethodCall] call to [] +# 78| getReceiver: [ConstantReadAccess] Array +# 78| getArgument: [IntegerLiteral] 1 +# 78| getArgument: [IntegerLiteral] 2 +# 78| getArgument: [IntegerLiteral] 3 +# 79| [ArrayLiteral] [...] +# 79| getDesugared: [MethodCall] call to [] +# 79| getReceiver: [ConstantReadAccess] Array +# 79| getArgument: [IntegerLiteral] 1 +# 79| getArgument: [IntegerLiteral] 2 +# 79| getArgument: [IntegerLiteral] 3 escape_sequences/escapes.rb: # 58| [ArrayLiteral] %w(...) # 58| getDesugared: [MethodCall] call to [] diff --git a/ruby/ql/test/library-tests/ast/TreeSitter.expected b/ruby/ql/test/library-tests/ast/TreeSitter.expected index 0a2ecda8d28..67a909d9002 100644 --- a/ruby/ql/test/library-tests/ast/TreeSitter.expected +++ b/ruby/ql/test/library-tests/ast/TreeSitter.expected @@ -1656,10 +1656,56 @@ constants/constants.rb: # 73| 1: [ReservedWord] :: # 73| 2: [Constant] FOURTY_SIX # 74| 5: [ReservedWord] end +# 78| 13: [Assignment] Assignment +# 78| 0: [Identifier] a +# 78| 1: [ReservedWord] = +# 78| 2: [Array] Array +# 78| 0: [ReservedWord] [ +# 78| 1: [Integer] 1 +# 78| 2: [ReservedWord] , +# 78| 3: [Integer] 2 +# 78| 4: [ReservedWord] , +# 78| 5: [Integer] 3 +# 78| 6: [ReservedWord] ] +# 79| 14: [Assignment] Assignment +# 79| 0: [Constant] A +# 79| 1: [ReservedWord] = +# 79| 2: [Array] Array +# 79| 0: [ReservedWord] [ +# 79| 1: [Integer] 1 +# 79| 2: [ReservedWord] , +# 79| 3: [Integer] 2 +# 79| 4: [ReservedWord] , +# 79| 5: [Integer] 3 +# 79| 6: [ReservedWord] ] +# 80| 15: [Assignment] Assignment +# 80| 0: [Constant] B +# 80| 1: [ReservedWord] = +# 80| 2: [Identifier] a +# 81| 16: [Assignment] Assignment +# 81| 0: [Constant] C +# 81| 1: [ReservedWord] = +# 81| 2: [Constant] A +# 82| 17: [Assignment] Assignment +# 82| 0: [Identifier] b +# 82| 1: [ReservedWord] = +# 82| 2: [Constant] B +# 84| 18: [If] If +# 84| 0: [ReservedWord] if +# 84| 1: [Identifier] condition +# 84| 2: [Then] Then +# 85| 0: [Assignment] Assignment +# 85| 0: [Identifier] c +# 85| 1: [ReservedWord] = +# 85| 2: [Identifier] b +# 86| 3: [ReservedWord] end +# 87| 19: [Identifier] c # 26| [Comment] # A call to Kernel::Array; despite beginning with an upper-case character, # 27| [Comment] # we don't consider this to be a constant access. # 55| [Comment] # refers to ::ModuleA::FOURTY_FOUR # 57| [Comment] # refers to ::ModuleA::ModuleB::ClassB::FOURTY_FOUR +# 76| [Comment] # Array constants +# 87| [Comment] # not recognised control/cases.rb: # 1| [Program] Program # 2| 0: [Assignment] Assignment diff --git a/ruby/ql/test/library-tests/ast/ValueText.expected b/ruby/ql/test/library-tests/ast/ValueText.expected index 6225b1b2e90..ecf7399a99a 100644 --- a/ruby/ql/test/library-tests/ast/ValueText.expected +++ b/ruby/ql/test/library-tests/ast/ValueText.expected @@ -109,6 +109,12 @@ exprValue | constants/constants.rb:63:19:63:20 | 45 | 45 | int | | constants/constants.rb:65:19:65:35 | FOURTY_FIVE | 45 | int | | constants/constants.rb:71:18:71:19 | 46 | 46 | int | +| constants/constants.rb:78:6:78:6 | 1 | 1 | int | +| constants/constants.rb:78:9:78:9 | 2 | 2 | int | +| constants/constants.rb:78:12:78:12 | 3 | 3 | int | +| constants/constants.rb:79:6:79:6 | 1 | 1 | int | +| constants/constants.rb:79:9:79:9 | 2 | 2 | int | +| constants/constants.rb:79:12:79:12 | 3 | 3 | int | | control/cases.rb:2:5:2:5 | 0 | 0 | int | | control/cases.rb:3:5:3:5 | 0 | 0 | int | | control/cases.rb:4:5:4:5 | 0 | 0 | int | @@ -1004,6 +1010,12 @@ exprCfgNodeValue | constants/constants.rb:63:19:63:20 | 45 | 45 | int | | constants/constants.rb:65:19:65:35 | FOURTY_FIVE | 45 | int | | constants/constants.rb:71:18:71:19 | 46 | 46 | int | +| constants/constants.rb:78:6:78:6 | 1 | 1 | int | +| constants/constants.rb:78:9:78:9 | 2 | 2 | int | +| constants/constants.rb:78:12:78:12 | 3 | 3 | int | +| constants/constants.rb:79:6:79:6 | 1 | 1 | int | +| constants/constants.rb:79:9:79:9 | 2 | 2 | int | +| constants/constants.rb:79:12:79:12 | 3 | 3 | int | | control/cases.rb:2:5:2:5 | 0 | 0 | int | | control/cases.rb:3:5:3:5 | 0 | 0 | int | | control/cases.rb:4:5:4:5 | 0 | 0 | int | From f7dca4d70fd58016671a3a0dde5158fd3a716993 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Tue, 12 Jul 2022 17:31:02 +0200 Subject: [PATCH 229/505] Swift: trap output rework Firstly, this change reworks how inter-process races are resolved. Moreover some responsability reorganization has led to merging `TrapArena` and `TrapOutput` again into a `TrapDomain` class. A `TargetFile` class is introduced, that is successfully created only for the first process that starts processing a given trap output file. From then on `TargetFile` simply wraps around `<<` stream operations, dumping them to a temporary file. When `TargetFile::commit` is called, the temporary file is moved on to the actual target trap file. Processes that lose the race can now just ignore the unneeded extraction and go on, while previously all processes would carry out all extractions overwriting each other at the end. Some of the file system logic contained in `SwiftExtractor.cpp` has been moved to this class, and two TODOs are solved: * introducing a better inter process file collision avoidance strategy * better error handling for trap output operations: if unable to write to the trap file (or carry out other basic file operations), we just abort. The changes to `ExprVisitor` and `StmtVisitor` are due to wanting to hide the raw `TrapDomain::createLabel` from them, and bring more funcionality under the generic caching/dispatching mechanism. --- swift/codegen/schema.yml | 1 + swift/extractor/SwiftExtractor.cpp | 70 +++++------------- swift/extractor/infra/BUILD.bazel | 2 + swift/extractor/infra/SwiftDispatcher.h | 46 ++++++------ swift/extractor/infra/SwiftTagTraits.h | 2 + swift/extractor/infra/TargetFile.cpp | 73 +++++++++++++++++++ swift/extractor/infra/TargetFile.h | 40 ++++++++++ swift/extractor/trap/TrapArena.h | 23 ------ .../trap/{TrapOutput.h => TrapDomain.h} | 68 +++++++++++------ swift/extractor/visitors/ExprVisitor.cpp | 10 +-- swift/extractor/visitors/StmtVisitor.cpp | 34 ++++----- swift/extractor/visitors/StmtVisitor.h | 4 +- swift/extractor/visitors/SwiftVisitor.h | 7 +- .../codeql/swift/generated/expr/Argument.qll | 4 +- swift/ql/lib/swift.dbscheme | 6 +- .../DotSyntaxCallExpr_getArgument.expected | 4 +- 16 files changed, 238 insertions(+), 156 deletions(-) create mode 100644 swift/extractor/infra/TargetFile.cpp create mode 100644 swift/extractor/infra/TargetFile.h delete mode 100644 swift/extractor/trap/TrapArena.h rename swift/extractor/trap/{TrapOutput.h => TrapDomain.h} (56%) diff --git a/swift/codegen/schema.yml b/swift/codegen/schema.yml index 01bb0c2feba..1c76a1c7a65 100644 --- a/swift/codegen/schema.yml +++ b/swift/codegen/schema.yml @@ -319,6 +319,7 @@ AppliedPropertyWrapperExpr: _extends: Expr Argument: + _extends: Locatable label: string _children: expr: Expr diff --git a/swift/extractor/SwiftExtractor.cpp b/swift/extractor/SwiftExtractor.cpp index 44d2309cb2a..88ad2152473 100644 --- a/swift/extractor/SwiftExtractor.cpp +++ b/swift/extractor/SwiftExtractor.cpp @@ -16,8 +16,9 @@ #include #include "swift/extractor/trap/generated/TrapClasses.h" -#include "swift/extractor/trap/TrapOutput.h" +#include "swift/extractor/trap/TrapDomain.h" #include "swift/extractor/visitors/SwiftVisitor.h" +#include "swift/extractor/infra/TargetFile.h" using namespace codeql; @@ -52,7 +53,7 @@ static void archiveFile(const SwiftExtractorConfiguration& config, swift::Source } } -static std::string getTrapFilename(swift::ModuleDecl& module, swift::SourceFile* primaryFile) { +static std::string getFilename(swift::ModuleDecl& module, swift::SourceFile* primaryFile) { if (primaryFile) { return primaryFile->getFilename().str(); } @@ -80,56 +81,39 @@ static void extractDeclarations(const SwiftExtractorConfiguration& config, swift::CompilerInstance& compiler, swift::ModuleDecl& module, swift::SourceFile* primaryFile = nullptr) { + auto filename = getFilename(module, primaryFile); + // The extractor can be called several times from different processes with - // the same input file(s) - // We are using PID to avoid concurrent access - // TODO: find a more robust approach to avoid collisions? - auto name = getTrapFilename(module, primaryFile); - llvm::StringRef filename(name); - std::string tempTrapName = filename.str() + '.' + std::to_string(getpid()) + ".trap"; - llvm::SmallString tempTrapPath(config.getTempTrapDir()); - llvm::sys::path::append(tempTrapPath, tempTrapName); - - llvm::StringRef tempTrapParent = llvm::sys::path::parent_path(tempTrapPath); - if (std::error_code ec = llvm::sys::fs::create_directories(tempTrapParent)) { - std::cerr << "Cannot create temp trap directory '" << tempTrapParent.str() - << "': " << ec.message() << "\n"; + // the same input file(s). Using `TargetFile` the first process will win, and the following + // will just skip the work + TargetFile trapStream{filename + ".trap", config.trapDir, config.getTempTrapDir()}; + if (!trapStream.good()) { + // another process arrived first, nothing to do for us return; } - std::ofstream trapStream(tempTrapPath.str().str()); - if (!trapStream) { - std::error_code ec; - ec.assign(errno, std::generic_category()); - std::cerr << "Cannot create temp trap file '" << tempTrapPath.str().str() - << "': " << ec.message() << "\n"; - return; - } trapStream << "/* extractor-args:\n"; - for (auto opt : config.frontendOptions) { + for (const auto& opt : config.frontendOptions) { trapStream << " " << std::quoted(opt) << " \\\n"; } trapStream << "\n*/\n"; trapStream << "/* swift-frontend-args:\n"; - for (auto opt : config.patchedFrontendOptions) { + for (const auto& opt : config.patchedFrontendOptions) { trapStream << " " << std::quoted(opt) << " \\\n"; } trapStream << "\n*/\n"; - TrapOutput trap{trapStream}; - TrapArena arena{}; + TrapDomain trap{trapStream}; // TODO: move default location emission elsewhere, possibly in a separate global trap file - auto unknownFileLabel = arena.allocateLabel(); // the following cannot conflict with actual files as those have an absolute path starting with / - trap.assignKey(unknownFileLabel, "unknown"); + auto unknownFileLabel = trap.createLabel("unknown"); + auto unknownLocationLabel = trap.createLabel("unknown"); trap.emit(FilesTrap{unknownFileLabel}); - auto unknownLocationLabel = arena.allocateLabel(); - trap.assignKey(unknownLocationLabel, "unknown"); trap.emit(LocationsTrap{unknownLocationLabel, unknownFileLabel}); - SwiftVisitor visitor(compiler.getSourceMgr(), arena, trap, module, primaryFile); + SwiftVisitor visitor(compiler.getSourceMgr(), trap, module, primaryFile); auto topLevelDecls = getTopLevelDecls(module, primaryFile); for (auto decl : topLevelDecls) { visitor.extract(decl); @@ -142,28 +126,10 @@ static void extractDeclarations(const SwiftExtractorConfiguration& config, // fact that the file was extracted llvm::SmallString name(filename); llvm::sys::fs::make_absolute(name); - auto fileLabel = arena.allocateLabel(); - trap.assignKey(fileLabel, name.str().str()); + auto fileLabel = trap.createLabel(name.str().str()); trap.emit(FilesTrap{fileLabel, name.str().str()}); } - - // TODO: Pick a better name to avoid collisions - std::string trapName = filename.str() + ".trap"; - llvm::SmallString trapPath(config.trapDir); - llvm::sys::path::append(trapPath, trapName); - - llvm::StringRef trapParent = llvm::sys::path::parent_path(trapPath); - if (std::error_code ec = llvm::sys::fs::create_directories(trapParent)) { - std::cerr << "Cannot create trap directory '" << trapParent.str() << "': " << ec.message() - << "\n"; - return; - } - - // TODO: The last process wins. Should we do better than that? - if (std::error_code ec = llvm::sys::fs::rename(tempTrapPath, trapPath)) { - std::cerr << "Cannot rename temp trap file '" << tempTrapPath.str().str() << "' -> '" - << trapPath.str().str() << "': " << ec.message() << "\n"; - } + trapStream.commit(); } static std::unordered_set collectInputFilenames(swift::CompilerInstance& compiler) { diff --git a/swift/extractor/infra/BUILD.bazel b/swift/extractor/infra/BUILD.bazel index 33098eb76a4..115fb06b745 100644 --- a/swift/extractor/infra/BUILD.bazel +++ b/swift/extractor/infra/BUILD.bazel @@ -2,9 +2,11 @@ load("//swift:rules.bzl", "swift_cc_library") swift_cc_library( name = "infra", + srcs = glob(["*.cpp"]), hdrs = glob(["*.h"]), visibility = ["//swift:__subpackages__"], deps = [ "//swift/extractor/trap", + "//swift/tools/prebuilt:swift-llvm-support", ], ) diff --git a/swift/extractor/infra/SwiftDispatcher.h b/swift/extractor/infra/SwiftDispatcher.h index c7ca43a1614..a9ecb6996ff 100644 --- a/swift/extractor/infra/SwiftDispatcher.h +++ b/swift/extractor/infra/SwiftDispatcher.h @@ -4,9 +4,8 @@ #include #include -#include "swift/extractor/trap/TrapArena.h" #include "swift/extractor/trap/TrapLabelStore.h" -#include "swift/extractor/trap/TrapOutput.h" +#include "swift/extractor/trap/TrapDomain.h" #include "swift/extractor/infra/SwiftTagTraits.h" #include "swift/extractor/trap/generated/TrapClasses.h" @@ -22,12 +21,10 @@ class SwiftDispatcher { // all references and pointers passed as parameters to this constructor are supposed to outlive // the SwiftDispatcher SwiftDispatcher(const swift::SourceManager& sourceManager, - TrapArena& arena, - TrapOutput& trap, + TrapDomain& trap, swift::ModuleDecl& currentModule, swift::SourceFile* currentPrimarySourceFile = nullptr) : sourceManager{sourceManager}, - arena{arena}, trap{trap}, currentModule{currentModule}, currentPrimarySourceFile{currentPrimarySourceFile} {} @@ -77,6 +74,7 @@ class SwiftDispatcher { } waitingForNewLabel = e; visit(e); + // TODO when everything is moved to structured C++ classes, this should be moved to createEntry if (auto l = store.get(e)) { if constexpr (!std::is_base_of_v) { attachLocation(e, *l); @@ -95,13 +93,17 @@ class SwiftDispatcher { return fetchLabelFromUnion(node); } + TrapLabel fetchLabel(const swift::StmtConditionElement& element) { + return fetchLabel(&element); + } + // Due to the lazy emission approach, we must assign a label to a corresponding AST node before // it actually gets emitted to handle recursive cases such as recursive calls, or recursive type // declarations template TrapLabelOf assignNewLabel(E* e, Args&&... args) { assert(waitingForNewLabel == Store::Handle{e} && "assignNewLabel called on wrong entity"); - auto label = createLabel>(std::forward(args)...); + auto label = trap.createLabel>(std::forward(args)...); store.insert(e, label); waitingForNewLabel = std::monostate{}; return label; @@ -118,18 +120,13 @@ class SwiftDispatcher { return TrapClassOf{assignNewLabel(&e, std::forward(args)...)}; } - template - TrapLabel createLabel() { - auto ret = arena.allocateLabel(); - trap.assignStar(ret); - return ret; - } - - template - TrapLabel createLabel(Args&&... args) { - auto ret = arena.allocateLabel(); - trap.assignKey(ret, std::forward(args)...); - return ret; + // used to create a new entry for entities that should not be cached + // an example is swift::Argument, that are created on the fly and thus have no stable pointer + template >* = nullptr> + auto createUncachedEntry(const E& e, Args&&... args) { + auto label = trap.createLabel>(std::forward(args)...); + attachLocation(&e, label); + return TrapClassOf{label}; } template @@ -213,6 +210,7 @@ class SwiftDispatcher { using Store = TrapLabelStore(filepath); + auto fileLabel = trap.createLabel(filepath); // TODO: do not emit duplicate trap entries for Files trap.emit(FilesTrap{fileLabel, filepath}); auto [startLine, startColumn] = sourceManager.getLineAndColumnInBuffer(start); auto [endLine, endColumn] = sourceManager.getLineAndColumnInBuffer(end); - auto locLabel = createLabel('{', fileLabel, "}:", startLine, ':', startColumn, ':', - endLine, ':', endColumn); + auto locLabel = trap.createLabel('{', fileLabel, "}:", startLine, ':', startColumn, + ':', endLine, ':', endColumn); trap.emit(LocationsTrap{locLabel, fileLabel, startLine, startColumn, endLine, endColumn}); trap.emit(LocatableLocationsTrap{locatableLabel, locLabel}); } @@ -275,7 +273,8 @@ class SwiftDispatcher { // which are to be introduced in follow-up PRs virtual void visit(swift::Decl* decl) = 0; virtual void visit(swift::Stmt* stmt) = 0; - virtual void visit(swift::StmtCondition* cond) = 0; + virtual void visit(const swift::StmtCondition* cond) = 0; + virtual void visit(const swift::StmtConditionElement* cond) = 0; virtual void visit(swift::CaseLabelItem* item) = 0; virtual void visit(swift::Expr* expr) = 0; virtual void visit(swift::Pattern* pattern) = 0; @@ -283,8 +282,7 @@ class SwiftDispatcher { virtual void visit(swift::TypeBase* type) = 0; const swift::SourceManager& sourceManager; - TrapArena& arena; - TrapOutput& trap; + TrapDomain& trap; Store store; Store::Handle waitingForNewLabel{std::monostate{}}; swift::ModuleDecl& currentModule; diff --git a/swift/extractor/infra/SwiftTagTraits.h b/swift/extractor/infra/SwiftTagTraits.h index 8a2e489683d..7447fa8f9b3 100644 --- a/swift/extractor/infra/SwiftTagTraits.h +++ b/swift/extractor/infra/SwiftTagTraits.h @@ -36,12 +36,14 @@ using SILBoxTypeReprTag = SilBoxTypeReprTag; MAP_TAG(Stmt); MAP_TAG(StmtCondition); +MAP_TYPE_TO_TAG(StmtConditionElement, ConditionElementTag); MAP_TAG(CaseLabelItem); #define ABSTRACT_STMT(CLASS, PARENT) MAP_SUBTAG(CLASS##Stmt, PARENT) #define STMT(CLASS, PARENT) ABSTRACT_STMT(CLASS, PARENT) #include MAP_TAG(Expr); +MAP_TAG(Argument); #define ABSTRACT_EXPR(CLASS, PARENT) MAP_SUBTAG(CLASS##Expr, PARENT) #define EXPR(CLASS, PARENT) ABSTRACT_EXPR(CLASS, PARENT) #include diff --git a/swift/extractor/infra/TargetFile.cpp b/swift/extractor/infra/TargetFile.cpp new file mode 100644 index 00000000000..5051c0c191f --- /dev/null +++ b/swift/extractor/infra/TargetFile.cpp @@ -0,0 +1,73 @@ +#include "swift/extractor/infra/TargetFile.h" + +#include +#include + +namespace codeql { +namespace { +[[noreturn]] void error(const char* action, const std::string& arg, std::error_code ec) { + std::cerr << "Unable to " << action << ": " << arg << " (" << ec.message() << ")\n"; + std::abort(); +} + +[[noreturn]] void error(const char* action, const std::string& arg) { + error(action, arg, {errno, std::system_category()}); +} + +void ensureParentDir(const std::string& path) { + auto parent = llvm::sys::path::parent_path(path); + if (auto ec = llvm::sys::fs::create_directories(parent)) { + error("create directory", parent.str(), ec); + } +} + +std::string initPath(std::string_view target, std::string_view dir) { + std::string ret{dir}; + assert(!target.empty() && "target must be a non-empty path"); + if (target[0] != '/') { + ret += '/'; + } + ret.append(target); + ensureParentDir(ret); + return ret; +} +} // namespace + +TargetFile::TargetFile(std::string_view target, + std::string_view targetDir, + std::string_view workingDir) + : workingPath{initPath(target, workingDir)}, targetPath{initPath(target, targetDir)} { + errno = 0; + // since C++17 "x" mode opens with O_EXCL (fails if file already exists) + if (auto f = std::fopen(targetPath.c_str(), "wx")) { + std::fclose(f); + out.open(workingPath); + checkOutput("open file for writing"); + } else { + if (errno != EEXIST) { + error("open file for writing", targetPath); + } + // else we just lost the race, do nothing (good() will return false to signal this) + } +} + +bool TargetFile::good() const { + return out && out.is_open(); +} + +void TargetFile::commit() { + assert(good()); + out.close(); + errno = 0; + if (std::rename(workingPath.c_str(), targetPath.c_str()) != 0) { + error("rename file", targetPath); + } +} + +void TargetFile::checkOutput(const char* action) { + if (!out) { + error(action, workingPath); + } +} + +} // namespace codeql diff --git a/swift/extractor/infra/TargetFile.h b/swift/extractor/infra/TargetFile.h new file mode 100644 index 00000000000..91a6f1651f1 --- /dev/null +++ b/swift/extractor/infra/TargetFile.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace codeql { + +// Only the first process trying to open an `TargetFile` for a given `target` is allowed to do +// so, all others will have an instance with `good() == false` and failing on any other operation. +// The content streamed to the `TargetFile` is written to `workingDir/target`, and is moved onto +// `targetDir/target` only when `commit()` is called +class TargetFile { + std::string workingPath; + std::string targetPath; + std::ofstream out; + + public: + TargetFile(std::string_view target, std::string_view targetDir, std::string_view workingDir); + + bool good() const; + void commit(); + + template + TargetFile& operator<<(T&& value) { + errno = 0; + out << value; + checkOutput("write to file"); + return *this; + } + + private: + void checkOutput(const char* action); +}; + +} // namespace codeql diff --git a/swift/extractor/trap/TrapArena.h b/swift/extractor/trap/TrapArena.h deleted file mode 100644 index 99d0b4a5d3b..00000000000 --- a/swift/extractor/trap/TrapArena.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "swift/extractor/trap/TrapLabel.h" - -namespace codeql { - -// TrapArena has the responsibilities to allocate distinct trap #-labels -// TODO this is now a small functionality that will be moved to code upcoming from other PRs -class TrapArena { - uint64_t id_{0}; - - public: - template - TrapLabel allocateLabel() { - return TrapLabel::unsafeCreateFromExplicitId(id_++); - } -}; - -} // namespace codeql diff --git a/swift/extractor/trap/TrapOutput.h b/swift/extractor/trap/TrapDomain.h similarity index 56% rename from swift/extractor/trap/TrapOutput.h rename to swift/extractor/trap/TrapDomain.h index 0e3f61d39e7..42aed53d499 100644 --- a/swift/extractor/trap/TrapOutput.h +++ b/swift/extractor/trap/TrapDomain.h @@ -2,18 +2,55 @@ #include #include "swift/extractor/trap/TrapLabel.h" +#include "swift/extractor/infra/TargetFile.h" namespace codeql { -// Sink for trap emissions and label assignments. This abstracts away `ofstream` operations -// like `ofstream`, an explicit bool operator is provided, that return false if something -// went wrong -// TODO better error handling -class TrapOutput { - std::ostream& out_; +// Abstracts a given trap output file, with its own universe of trap labels +class TrapDomain { + TargetFile& out_; public: - explicit TrapOutput(std::ostream& out) : out_{out} {} + explicit TrapDomain(TargetFile& out) : out_{out} {} + + template + void emit(const Entry& e) { + print(e); + } + + template + void debug(const Args&... args) { + print("/* DEBUG:"); + print(args...); + print("*/"); + } + + template + TrapLabel createLabel() { + auto ret = allocateLabel(); + assignStar(ret); + return ret; + } + + template + TrapLabel createLabel(Args&&... args) { + auto ret = allocateLabel(); + assignKey(ret, std::forward(args)...); + return ret; + } + + private: + uint64_t id_{0}; + + template + TrapLabel allocateLabel() { + return TrapLabel::unsafeCreateFromExplicitId(id_++); + } + + template + void print(const Args&... args) { + (out_ << ... << args) << '\n'; + } template void assignStar(TrapLabel label) { @@ -33,23 +70,6 @@ class TrapOutput { (oss << ... << keyParts); assignKey(label, oss.str()); } - - template - void emit(const Entry& e) { - print(e); - } - - template - void debug(const Args&... args) { - out_ << "/* DEBUG:\n"; - (out_ << ... << args) << "\n*/\n"; - } - - private: - template - void print(const Args&... args) { - (out_ << ... << args) << '\n'; - } }; } // namespace codeql diff --git a/swift/extractor/visitors/ExprVisitor.cpp b/swift/extractor/visitors/ExprVisitor.cpp index e6aabef4cea..730996a2739 100644 --- a/swift/extractor/visitors/ExprVisitor.cpp +++ b/swift/extractor/visitors/ExprVisitor.cpp @@ -611,11 +611,11 @@ void ExprVisitor::fillAbstractClosureExpr(const swift::AbstractClosureExpr& expr } TrapLabel ExprVisitor::emitArgument(const swift::Argument& arg) { - auto argLabel = dispatcher_.createLabel(); - assert(arg.getExpr() && "Argument has getExpr"); - dispatcher_.emit( - ArgumentsTrap{argLabel, arg.getLabel().str().str(), dispatcher_.fetchLabel(arg.getExpr())}); - return argLabel; + auto entry = dispatcher_.createUncachedEntry(arg); + entry.label = arg.getLabel().str().str(); + entry.expr = dispatcher_.fetchLabel(arg.getExpr()); + dispatcher_.emit(entry); + return entry.id; } void ExprVisitor::emitImplicitConversionExpr(swift::ImplicitConversionExpr* expr, diff --git a/swift/extractor/visitors/StmtVisitor.cpp b/swift/extractor/visitors/StmtVisitor.cpp index 21f93828ed7..bc8a8a30933 100644 --- a/swift/extractor/visitors/StmtVisitor.cpp +++ b/swift/extractor/visitors/StmtVisitor.cpp @@ -7,26 +7,22 @@ void StmtVisitor::visitLabeledStmt(swift::LabeledStmt* stmt) { emitLabeledStmt(stmt, label); } -void StmtVisitor::visitStmtCondition(swift::StmtCondition* cond) { - auto label = dispatcher_.assignNewLabel(cond); - dispatcher_.emit(StmtConditionsTrap{label}); - unsigned index = 0; - for (const auto& cond : *cond) { - auto condLabel = dispatcher_.createLabel(); - dispatcher_.attachLocation(cond, condLabel); - dispatcher_.emit(ConditionElementsTrap{condLabel}); - dispatcher_.emit(StmtConditionElementsTrap{label, index++, condLabel}); - if (auto boolean = cond.getBooleanOrNull()) { - auto elementLabel = dispatcher_.fetchLabel(boolean); - dispatcher_.emit(ConditionElementBooleansTrap{condLabel, elementLabel}); - } else if (auto pattern = cond.getPatternOrNull()) { - auto patternLabel = dispatcher_.fetchLabel(pattern); - auto initilizerLabel = dispatcher_.fetchLabel(cond.getInitializer()); - dispatcher_.emit(ConditionElementPatternsTrap{condLabel, patternLabel}); - dispatcher_.emit(ConditionElementInitializersTrap{condLabel, initilizerLabel}); - } - /// TODO: Implement availability +codeql::StmtCondition StmtVisitor::translateStmtCondition(const swift::StmtCondition& cond) { + auto entry = dispatcher_.createEntry(cond); + entry.elements = dispatcher_.fetchRepeatedLabels(cond); + return entry; +} + +codeql::ConditionElement StmtVisitor::translateStmtConditionElement( + const swift::StmtConditionElement& element) { + auto entry = dispatcher_.createEntry(element); + if (auto boolean = element.getBooleanOrNull()) { + entry.boolean = dispatcher_.fetchLabel(boolean); + } else if (auto pattern = element.getPatternOrNull()) { + entry.pattern = dispatcher_.fetchLabel(pattern); + entry.initializer = dispatcher_.fetchLabel(element.getInitializer()); } + return entry; } void StmtVisitor::visitLabeledConditionalStmt(swift::LabeledConditionalStmt* stmt) { diff --git a/swift/extractor/visitors/StmtVisitor.h b/swift/extractor/visitors/StmtVisitor.h index 78273366d9e..e929a364399 100644 --- a/swift/extractor/visitors/StmtVisitor.h +++ b/swift/extractor/visitors/StmtVisitor.h @@ -10,7 +10,9 @@ class StmtVisitor : public AstVisitorBase { using AstVisitorBase::AstVisitorBase; void visitLabeledStmt(swift::LabeledStmt* stmt); - void visitStmtCondition(swift::StmtCondition* cond); + codeql::StmtCondition translateStmtCondition(const swift::StmtCondition& cond); + codeql::ConditionElement translateStmtConditionElement( + const swift::StmtConditionElement& element); void visitLabeledConditionalStmt(swift::LabeledConditionalStmt* stmt); void visitCaseLabelItem(swift::CaseLabelItem* labelItem); void visitBraceStmt(swift::BraceStmt* stmt); diff --git a/swift/extractor/visitors/SwiftVisitor.h b/swift/extractor/visitors/SwiftVisitor.h index 6380390af82..a13479246f4 100644 --- a/swift/extractor/visitors/SwiftVisitor.h +++ b/swift/extractor/visitors/SwiftVisitor.h @@ -22,7 +22,12 @@ class SwiftVisitor : private SwiftDispatcher { private: void visit(swift::Decl* decl) override { declVisitor.visit(decl); } void visit(swift::Stmt* stmt) override { stmtVisitor.visit(stmt); } - void visit(swift::StmtCondition* cond) override { stmtVisitor.visitStmtCondition(cond); } + void visit(const swift::StmtCondition* cond) override { + emit(stmtVisitor.translateStmtCondition(*cond)); + } + void visit(const swift::StmtConditionElement* element) override { + emit(stmtVisitor.translateStmtConditionElement(*element)); + } void visit(swift::CaseLabelItem* item) override { stmtVisitor.visitCaseLabelItem(item); } void visit(swift::Expr* expr) override { exprVisitor.visit(expr); } void visit(swift::Pattern* pattern) override { patternVisitor.visit(pattern); } diff --git a/swift/ql/lib/codeql/swift/generated/expr/Argument.qll b/swift/ql/lib/codeql/swift/generated/expr/Argument.qll index 9f16b90e3fe..7e8d6ea0ed3 100644 --- a/swift/ql/lib/codeql/swift/generated/expr/Argument.qll +++ b/swift/ql/lib/codeql/swift/generated/expr/Argument.qll @@ -1,8 +1,8 @@ // generated by codegen/codegen.py -import codeql.swift.elements.Element import codeql.swift.elements.expr.Expr +import codeql.swift.elements.Locatable -class ArgumentBase extends @argument, Element { +class ArgumentBase extends @argument, Locatable { override string getAPrimaryQlClass() { result = "Argument" } string getLabel() { arguments(this, result, _) } diff --git a/swift/ql/lib/swift.dbscheme b/swift/ql/lib/swift.dbscheme index 83ee8412131..ae57f4e6fbb 100644 --- a/swift/ql/lib/swift.dbscheme +++ b/swift/ql/lib/swift.dbscheme @@ -13,8 +13,7 @@ sourceLocationPrefix( // from codegen/schema.yml @element = - @argument -| @callable + @callable | @file | @generic_context | @iterable_decl_context @@ -34,7 +33,8 @@ files( ); @locatable = - @ast_node + @argument +| @ast_node | @condition_element ; diff --git a/swift/ql/test/extractor-tests/generated/expr/DotSyntaxCallExpr/DotSyntaxCallExpr_getArgument.expected b/swift/ql/test/extractor-tests/generated/expr/DotSyntaxCallExpr/DotSyntaxCallExpr_getArgument.expected index 64bdae371b7..85102771b48 100644 --- a/swift/ql/test/extractor-tests/generated/expr/DotSyntaxCallExpr/DotSyntaxCallExpr_getArgument.expected +++ b/swift/ql/test/extractor-tests/generated/expr/DotSyntaxCallExpr/DotSyntaxCallExpr_getArgument.expected @@ -1,2 +1,2 @@ -| dot_syntax_call.swift:6:1:6:3 | call to foo(_:_:) | 0 | : X.Type | -| dot_syntax_call.swift:7:1:7:3 | call to bar() | 0 | : X.Type | +| dot_syntax_call.swift:6:1:6:3 | call to foo(_:_:) | 0 | dot_syntax_call.swift:6:1:6:1 | : X.Type | +| dot_syntax_call.swift:7:1:7:3 | call to bar() | 0 | dot_syntax_call.swift:7:1:7:1 | : X.Type | From 5773a734c307d656b52db8ac75e842f41821fb79 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Wed, 13 Jul 2022 11:27:50 +0200 Subject: [PATCH 230/505] Swift: slightly simplify a cppgen change --- swift/codegen/generators/cppgen.py | 38 +++++++++++++----------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/swift/codegen/generators/cppgen.py b/swift/codegen/generators/cppgen.py index 08af5fb82f9..420e82605e1 100644 --- a/swift/codegen/generators/cppgen.py +++ b/swift/codegen/generators/cppgen.py @@ -13,7 +13,6 @@ Each class in the schema gets a corresponding `struct` in `TrapClasses.h`, where import functools import pathlib -import typing from typing import Dict import inflection @@ -35,25 +34,22 @@ def _get_type(t: str) -> str: return t -def _get_fields(cls: schema.Class) -> typing.Iterable[cpp.Field]: - for p in cls.properties: - if "cpp_skip" in p.pragmas: - continue - trap_name = None - if not p.is_single: - trap_name = inflection.camelize(f"{cls.name}_{p.name}") - if not p.is_predicate: - trap_name = inflection.pluralize(trap_name) - args = dict( - field_name=p.name + ("_" if p.name in cpp.cpp_keywords else ""), - type=_get_type(p.type), - is_optional=p.is_optional, - is_repeated=p.is_repeated, - is_predicate=p.is_predicate, - trap_name=trap_name, - ) - args.update(cpp.get_field_override(p.name)) - yield cpp.Field(**args) +def _get_field(cls: schema.Class, p: schema.Property) -> cpp.Field: + trap_name = None + if not p.is_single: + trap_name = inflection.camelize(f"{cls.name}_{p.name}") + if not p.is_predicate: + trap_name = inflection.pluralize(trap_name) + args = dict( + field_name=p.name + ("_" if p.name in cpp.cpp_keywords else ""), + type=_get_type(p.type), + is_optional=p.is_optional, + is_repeated=p.is_repeated, + is_predicate=p.is_predicate, + trap_name=trap_name, + ) + args.update(cpp.get_field_override(p.name)) + return cpp.Field(**args) class Processor: @@ -69,7 +65,7 @@ class Processor: return cpp.Class( name=name, bases=[self._get_class(b) for b in cls.bases], - fields=list(_get_fields(cls)), + fields=[_get_field(cls, p) for p in cls.properties if "cpp_skip" not in p.pragmas], final=not cls.derived, trap_name=trap_name, ) From b1dd3c2d846664a7876ff06042590963c070244b Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Wed, 13 Jul 2022 13:58:36 +0100 Subject: [PATCH 231/505] Model java.util.Properties.getProperty --- .../code/java/dataflow/internal/ContainerFlow.qll | 3 +++ java/ql/lib/semmle/code/java/frameworks/Properties.qll | 4 +--- .../test/library-tests/dataflow/collections/Test.java | 10 ++++++++++ .../library-tests/dataflow/collections/flow.expected | 3 +++ 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/ContainerFlow.qll b/java/ql/lib/semmle/code/java/dataflow/internal/ContainerFlow.qll index 0f5d5e39f39..e9c457fc801 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/ContainerFlow.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/ContainerFlow.qll @@ -241,6 +241,9 @@ private class ContainerFlowSummaries extends SummaryModelCsv { "java.util;NavigableSet;true;pollLast;();;Argument[-1].Element;ReturnValue;value;manual", "java.util;NavigableSet;true;subSet;(Object,boolean,Object,boolean);;Argument[-1].Element;ReturnValue.Element;value;manual", "java.util;NavigableSet;true;tailSet;(Object,boolean);;Argument[-1].Element;ReturnValue.Element;value;manual", + "java.util;Properties;true;getProperty;(String);;Argument[-1].MapValue;ReturnValue;value;manual", + "java.util;Properties;true;getProperty;(String,String);;Argument[-1].MapValue;ReturnValue;value;manual", + "java.util;Properties;true;getProperty;(String,String);;Argument[1];ReturnValue;value;manual", "java.util;Scanner;true;next;(Pattern);;Argument[-1];ReturnValue;taint;manual", "java.util;Scanner;true;next;(String);;Argument[-1];ReturnValue;taint;manual", "java.util;SortedMap;true;headMap;(Object);;Argument[-1].MapKey;ReturnValue.MapKey;value;manual", diff --git a/java/ql/lib/semmle/code/java/frameworks/Properties.qll b/java/ql/lib/semmle/code/java/frameworks/Properties.qll index 7b749a13e05..0c7b83b2e52 100644 --- a/java/ql/lib/semmle/code/java/frameworks/Properties.qll +++ b/java/ql/lib/semmle/code/java/frameworks/Properties.qll @@ -10,13 +10,11 @@ class TypeProperty extends Class { } /** The `getProperty` method of the class `java.util.Properties`. */ -class PropertiesGetPropertyMethod extends ValuePreservingMethod { +class PropertiesGetPropertyMethod extends Method { PropertiesGetPropertyMethod() { getDeclaringType() instanceof TypeProperty and hasName("getProperty") } - - override predicate returnsValue(int arg) { arg = 1 } } /** The `get` method of the class `java.util.Properties`. */ diff --git a/java/ql/test/library-tests/dataflow/collections/Test.java b/java/ql/test/library-tests/dataflow/collections/Test.java index 216f373ca8c..9836070f42c 100644 --- a/java/ql/test/library-tests/dataflow/collections/Test.java +++ b/java/ql/test/library-tests/dataflow/collections/Test.java @@ -78,4 +78,14 @@ public class Test { sink(x18); // Flow }); } + + public void run4() { + Properties p = new Properties(); + p.put("key", tainted); + sink(p.getProperty("key")); // Flow + sink(p.getProperty("key", "defaultValue")); // Flow + + Properties clean = new Properties(); + sink(clean.getProperty("key", tainted)); // Flow + } } diff --git a/java/ql/test/library-tests/dataflow/collections/flow.expected b/java/ql/test/library-tests/dataflow/collections/flow.expected index 1a4bed0a6e7..875a4191722 100644 --- a/java/ql/test/library-tests/dataflow/collections/flow.expected +++ b/java/ql/test/library-tests/dataflow/collections/flow.expected @@ -11,3 +11,6 @@ | Test.java:49:20:49:26 | tainted | Test.java:60:12:60:14 | x14 | | Test.java:73:11:73:17 | tainted | Test.java:75:10:75:12 | x17 | | Test.java:73:11:73:17 | tainted | Test.java:78:12:78:14 | x18 | +| Test.java:84:18:84:24 | tainted | Test.java:85:10:85:29 | getProperty(...) | +| Test.java:84:18:84:24 | tainted | Test.java:86:10:86:45 | getProperty(...) | +| Test.java:89:35:89:41 | tainted | Test.java:89:10:89:42 | getProperty(...) | From f9da4a045624ad49c755a9f880dc7713a6729dbd Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Wed, 13 Jul 2022 14:11:31 +0100 Subject: [PATCH 232/505] Add change note --- java/ql/lib/change-notes/2022-07-13-properites.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 java/ql/lib/change-notes/2022-07-13-properites.md diff --git a/java/ql/lib/change-notes/2022-07-13-properites.md b/java/ql/lib/change-notes/2022-07-13-properites.md new file mode 100644 index 00000000000..2be74455704 --- /dev/null +++ b/java/ql/lib/change-notes/2022-07-13-properites.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Added data-flow models for `java.util.Properites`. Additional results may be found where relevant data is stored in and then retrieved from a `Properties` instance. From f7c47b6c75f79430e75147d224ce5049352be8ee Mon Sep 17 00:00:00 2001 From: Raul Garcia <42392023+raulgarciamsft@users.noreply.github.com> Date: Wed, 13 Jul 2022 08:34:48 -0700 Subject: [PATCH 233/505] Update python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.py Co-authored-by: Taus --- .../CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.py b/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.py index b7099b3d6c0..96aeb436a9b 100644 --- a/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.py +++ b/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.py @@ -3,5 +3,5 @@ blob_client.require_encryption = True blob_client.key_encryption_key = kek # GOOD: Must use `encryption_version` set to `2.0` blob_client.encryption_version = '2.0' # Use Version 2.0! -with open(“decryptedcontentfile.txtâ€, “rbâ€) as stream: +with open("decryptedcontentfile.txt", "rb") as stream: blob_client.upload_blob(stream, overwrite=OVERWRITE_EXISTING) \ No newline at end of file From 2cc703387b0c42a7c260cc5e400d08e9a7c08ce2 Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Thu, 14 Jul 2022 00:05:19 +0000 Subject: [PATCH 234/505] use taint config for data flow --- .../ManuallyCheckHttpVerb.ql | 48 ++++++++----------- .../ManuallyCheckHttpVerb.expected | 32 +++++++++++++ .../ManuallyCheckHttpVerb.rb | 18 ++++++- 3 files changed, 70 insertions(+), 28 deletions(-) diff --git a/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql b/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql index 72574e148d2..3de3a7a03d5 100644 --- a/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql +++ b/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql @@ -1,7 +1,7 @@ /** * @name Manually checking http verb instead of using built in rails routes and protections * @description Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. - * @kind problem + * @kind path-problem * @problem.severity error * @security-severity 5.0 * @precision low @@ -13,6 +13,8 @@ import ruby import codeql.ruby.DataFlow import codeql.ruby.controlflow.CfgNodes import codeql.ruby.frameworks.ActionController +import codeql.ruby.TaintTracking +import DataFlow::PathGraph // any `request` calls in an action method class Request extends DataFlow::CallNode { @@ -70,32 +72,24 @@ class RequestGet extends DataFlow::CallNode { } } -// A conditional expression where the condition uses `request.method`, `request.request_method`, `request.raw_request_method`, `request.request_method_symbol`, or `request.get?` in some way. -// e.g. -// ``` -// r = request.request_method -// if r == "GET" -// ... -// ``` -class RequestMethodConditional extends DataFlow::Node { - RequestMethodConditional() { - // We have to cast the dataflow node down to a specific CFG node (`ExprNodes::ConditionalExprCfgNode`) to be able to call `getCondition()`. - // We then find the dataflow node corresponding to the condition CFG node, - // and filter for just nodes where a request method accessor value flows to them. - exists(DataFlow::Node conditionNode | - conditionNode.asExpr() = this.asExpr().(ExprNodes::ConditionalExprCfgNode).getCondition() - | - ( - any(RequestMethod r).flowsTo(conditionNode) or - any(RequestRequestMethod r).flowsTo(conditionNode) or - any(RequestRawRequestMethod r).flowsTo(conditionNode) or - any(RequestRequestMethodSymbol r).flowsTo(conditionNode) or - any(RequestGet r).flowsTo(conditionNode) - ) - ) +class HttpVerbConfig extends TaintTracking::Configuration { + HttpVerbConfig() { this = "HttpVerbConfig" } + + override predicate isSource(DataFlow::Node source) { + source instanceof RequestMethod or + source instanceof RequestRequestMethod or + source instanceof RequestEnvMethod or + source instanceof RequestRawRequestMethod or + source instanceof RequestRequestMethodSymbol or + source instanceof RequestGet + } + + override predicate isSink(DataFlow::Node sink) { + exists(ExprNodes::ConditionalExprCfgNode c | c.getCondition() = sink.asExpr()) or + exists(ExprNodes::CaseExprCfgNode c | c.getValue() = sink.asExpr()) } } -from RequestMethodConditional req -select req, - "Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods." +from HttpVerbConfig config, DataFlow::Node source, DataFlow::Node sink +where config.hasFlow(source, sink) +select sink.asExpr().getExpr(), source, sink, "Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods." diff --git a/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.expected b/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.expected index e69de29bb2d..9102005a67e 100644 --- a/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.expected +++ b/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.expected @@ -0,0 +1,32 @@ +edges +| ManuallyCheckHttpVerb.rb:11:14:11:24 | call to env : | ManuallyCheckHttpVerb.rb:11:14:11:42 | ...[...] : | +| ManuallyCheckHttpVerb.rb:11:14:11:42 | ...[...] : | ManuallyCheckHttpVerb.rb:12:8:12:22 | ... == ... | +| ManuallyCheckHttpVerb.rb:19:14:19:35 | call to request_method : | ManuallyCheckHttpVerb.rb:20:8:20:22 | ... == ... | +| ManuallyCheckHttpVerb.rb:27:14:27:27 | call to method : | ManuallyCheckHttpVerb.rb:28:8:28:22 | ... == ... | +| ManuallyCheckHttpVerb.rb:35:14:35:39 | call to raw_request_method : | ManuallyCheckHttpVerb.rb:36:8:36:22 | ... == ... | +| ManuallyCheckHttpVerb.rb:51:16:51:44 | call to request_method_symbol : | ManuallyCheckHttpVerb.rb:52:10:52:23 | ... == ... | +| ManuallyCheckHttpVerb.rb:59:10:59:20 | call to env : | ManuallyCheckHttpVerb.rb:59:10:59:38 | ...[...] | +nodes +| ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? | semmle.label | call to get? | +| ManuallyCheckHttpVerb.rb:11:14:11:24 | call to env : | semmle.label | call to env : | +| ManuallyCheckHttpVerb.rb:11:14:11:42 | ...[...] : | semmle.label | ...[...] : | +| ManuallyCheckHttpVerb.rb:12:8:12:22 | ... == ... | semmle.label | ... == ... | +| ManuallyCheckHttpVerb.rb:19:14:19:35 | call to request_method : | semmle.label | call to request_method : | +| ManuallyCheckHttpVerb.rb:20:8:20:22 | ... == ... | semmle.label | ... == ... | +| ManuallyCheckHttpVerb.rb:27:14:27:27 | call to method : | semmle.label | call to method : | +| ManuallyCheckHttpVerb.rb:28:8:28:22 | ... == ... | semmle.label | ... == ... | +| ManuallyCheckHttpVerb.rb:35:14:35:39 | call to raw_request_method : | semmle.label | call to raw_request_method : | +| ManuallyCheckHttpVerb.rb:36:8:36:22 | ... == ... | semmle.label | ... == ... | +| ManuallyCheckHttpVerb.rb:51:16:51:44 | call to request_method_symbol : | semmle.label | call to request_method_symbol : | +| ManuallyCheckHttpVerb.rb:52:10:52:23 | ... == ... | semmle.label | ... == ... | +| ManuallyCheckHttpVerb.rb:59:10:59:20 | call to env : | semmle.label | call to env : | +| ManuallyCheckHttpVerb.rb:59:10:59:38 | ...[...] | semmle.label | ...[...] | +subpaths +#select +| ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? | ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? | ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | +| ManuallyCheckHttpVerb.rb:12:8:12:22 | ... == ... | ManuallyCheckHttpVerb.rb:11:14:11:24 | call to env | ManuallyCheckHttpVerb.rb:12:8:12:22 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | +| ManuallyCheckHttpVerb.rb:20:8:20:22 | ... == ... | ManuallyCheckHttpVerb.rb:19:14:19:35 | call to request_method | ManuallyCheckHttpVerb.rb:20:8:20:22 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | +| ManuallyCheckHttpVerb.rb:28:8:28:22 | ... == ... | ManuallyCheckHttpVerb.rb:27:14:27:27 | call to method | ManuallyCheckHttpVerb.rb:28:8:28:22 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | +| ManuallyCheckHttpVerb.rb:36:8:36:22 | ... == ... | ManuallyCheckHttpVerb.rb:35:14:35:39 | call to raw_request_method | ManuallyCheckHttpVerb.rb:36:8:36:22 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | +| ManuallyCheckHttpVerb.rb:52:10:52:23 | ... == ... | ManuallyCheckHttpVerb.rb:51:16:51:44 | call to request_method_symbol | ManuallyCheckHttpVerb.rb:52:10:52:23 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | +| ManuallyCheckHttpVerb.rb:59:10:59:38 | ...[...] | ManuallyCheckHttpVerb.rb:59:10:59:20 | call to env | ManuallyCheckHttpVerb.rb:59:10:59:38 | ...[...] | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | diff --git a/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.rb b/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.rb index ad0f5b45566..055e9d98638 100644 --- a/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.rb +++ b/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.rb @@ -38,6 +38,14 @@ class ExampleController < ActionController::Base end end + # Should not find + def baz2 + method = request.raw_request_method + if some_other_function == "GET" + Resource.find(id: params[:id]) + end + end + # Should find def foobarbaz method = request.request_method_symbol @@ -56,7 +64,15 @@ class ExampleController < ActionController::Base end end - + # Should not find + def resource_action + case request.random_method + when "GET" + Resource.find(id: params[:id]) + when "POST" + Resource.new(id: params[:id], details: params[:details]) + end + end end class SafeController < ActionController::Base From ae634367c99f404fb3f2a92944243ccef9d50b95 Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Thu, 14 Jul 2022 00:11:25 +0000 Subject: [PATCH 235/505] add qhelp file --- .../ManuallyCheckHttpVerb.qhelp | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.qhelp diff --git a/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.qhelp b/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.qhelp new file mode 100644 index 00000000000..5d7378ef003 --- /dev/null +++ b/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.qhelp @@ -0,0 +1,23 @@ + + + +

    + Manually checking the HTTP request verb inside of a controller method can lead to + CSRF bypass if GET or HEAD requests are handled improperly. +

    +
    + +

    + It is better to use different controller methods for each resource/http verb combination + and configure the Rails routes in your application to call them accordingly. +

    +
    + + +

    + See https://guides.rubyonrails.org/routing.html for more information. +

    +
    +
    From ee79834cc80671d5e4535969e600346e4ea6708e Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Thu, 14 Jul 2022 00:15:39 +0000 Subject: [PATCH 236/505] formatting in qhelp --- .../manually-check-http-verb/ManuallyCheckHttpVerb.qhelp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.qhelp b/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.qhelp index 5d7378ef003..d50c7f0bf30 100644 --- a/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.qhelp +++ b/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.qhelp @@ -16,8 +16,8 @@ -

    +

  • See https://guides.rubyonrails.org/routing.html for more information. -

    +
  • From 9a186ba5d2a524bc1e04851d22fcace19f1100f6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 14 Jul 2022 00:18:56 +0000 Subject: [PATCH 237/505] Add changed framework coverage reports --- java/documentation/library-coverage/coverage.csv | 2 +- java/documentation/library-coverage/coverage.rst | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/java/documentation/library-coverage/coverage.csv b/java/documentation/library-coverage/coverage.csv index 06ec8d978d4..ce777afae1d 100644 --- a/java/documentation/library-coverage/coverage.csv +++ b/java/documentation/library-coverage/coverage.csv @@ -36,7 +36,7 @@ java.lang,13,,58,,,,,,,,,,,8,,,,,4,,,1,,,,,,,,,,,,,,,46,12 java.net,10,3,7,,,,,,,,,,,,,,10,,,,,,,,,,,,,,,,,,,3,7, java.nio,15,,6,,13,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,6, java.sql,11,,,,,,,,,4,,,,,,,,,,,,,,,,7,,,,,,,,,,,, -java.util,44,,438,,,,,,,,,,,34,,,,,,5,2,,1,2,,,,,,,,,,,,,24,414 +java.util,44,,441,,,,,,,,,,,34,,,,,,5,2,,1,2,,,,,,,,,,,,,24,417 javax.faces.context,2,7,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,7,, javax.jms,,9,57,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,9,57, javax.json,,,123,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,100,23 diff --git a/java/documentation/library-coverage/coverage.rst b/java/documentation/library-coverage/coverage.rst index 7b45a3115b1..3e4ea1201bf 100644 --- a/java/documentation/library-coverage/coverage.rst +++ b/java/documentation/library-coverage/coverage.rst @@ -15,9 +15,9 @@ Java framework & library support `Apache HttpComponents `_,"``org.apache.hc.core5.*``, ``org.apache.http``",5,136,28,,,3,,,,25 `Google Guava `_,``com.google.common.*``,,728,39,,6,,,,, `JSON-java `_,``org.json``,,236,,,,,,,, - Java Standard Library,``java.*``,3,549,130,28,,,7,,,10 + Java Standard Library,``java.*``,3,552,130,28,,,7,,,10 Java extensions,"``javax.*``, ``jakarta.*``",63,609,32,,,4,,1,1,2 `Spring `_,``org.springframework.*``,29,476,101,,,,19,14,,29 Others,"``androidx.slice``, ``cn.hutool.core.codec``, ``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.fasterxml.jackson.core``, ``com.fasterxml.jackson.databind``, ``com.opensymphony.xwork2.ognl``, ``com.rabbitmq.client``, ``com.unboundid.ldap.sdk``, ``com.zaxxer.hikari``, ``flexjson``, ``groovy.lang``, ``groovy.util``, ``jodd.json``, ``kotlin.jvm.internal``, ``net.sf.saxon.s9api``, ``ognl``, ``okhttp3``, ``org.apache.commons.codec``, ``org.apache.commons.jexl2``, ``org.apache.commons.jexl3``, ``org.apache.commons.logging``, ``org.apache.commons.ognl``, ``org.apache.directory.ldap.client.api``, ``org.apache.ibatis.jdbc``, ``org.apache.log4j``, ``org.apache.logging.log4j``, ``org.apache.shiro.codec``, ``org.apache.shiro.jndi``, ``org.codehaus.groovy.control``, ``org.dom4j``, ``org.hibernate``, ``org.jboss.logging``, ``org.jdbi.v3.core``, ``org.jooq``, ``org.mvel2``, ``org.scijava.log``, ``org.slf4j``, ``org.xml.sax``, ``org.xmlpull.v1``, ``play.mvc``, ``ratpack.core.form``, ``ratpack.core.handling``, ``ratpack.core.http``, ``ratpack.exec``, ``ratpack.form``, ``ratpack.func``, ``ratpack.handling``, ``ratpack.http``, ``ratpack.util``, ``retrofit2``",65,395,932,,,,14,18,,3 - Totals,,217,6410,1474,117,6,10,107,33,1,84 + Totals,,217,6413,1474,117,6,10,107,33,1,84 From 3dd61cadf44297f672a9e550b289781abf325839 Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Thu, 14 Jul 2022 00:19:36 +0000 Subject: [PATCH 238/505] formatting query --- .../ManuallyCheckHttpVerb.ql | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql b/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql index 3de3a7a03d5..e7553b39a3d 100644 --- a/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql +++ b/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql @@ -74,14 +74,14 @@ class RequestGet extends DataFlow::CallNode { class HttpVerbConfig extends TaintTracking::Configuration { HttpVerbConfig() { this = "HttpVerbConfig" } - + override predicate isSource(DataFlow::Node source) { - source instanceof RequestMethod or - source instanceof RequestRequestMethod or - source instanceof RequestEnvMethod or - source instanceof RequestRawRequestMethod or - source instanceof RequestRequestMethodSymbol or - source instanceof RequestGet + source instanceof RequestMethod or + source instanceof RequestRequestMethod or + source instanceof RequestEnvMethod or + source instanceof RequestRawRequestMethod or + source instanceof RequestRequestMethodSymbol or + source instanceof RequestGet } override predicate isSink(DataFlow::Node sink) { @@ -92,4 +92,5 @@ class HttpVerbConfig extends TaintTracking::Configuration { from HttpVerbConfig config, DataFlow::Node source, DataFlow::Node sink where config.hasFlow(source, sink) -select sink.asExpr().getExpr(), source, sink, "Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods." +select sink.asExpr().getExpr(), source, sink, + "Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods." From 8ca7d7d775cf8540acbb7539fad7830c04eb3ed2 Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Thu, 14 Jul 2022 00:22:38 +0000 Subject: [PATCH 239/505] update change note --- ruby/ql/lib/change-notes/released/0.3.1.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruby/ql/lib/change-notes/released/0.3.1.md b/ruby/ql/lib/change-notes/released/0.3.1.md index 64efa15884a..392aa6f0b27 100644 --- a/ruby/ql/lib/change-notes/released/0.3.1.md +++ b/ruby/ql/lib/change-notes/released/0.3.1.md @@ -2,4 +2,4 @@ ### Minor Analysis Improvements -- Calls to `ActiveRecord::Relation#annotate` have now been added to `ActiveRecordModelClass#sqlFragmentArgument` so that it can be used as a sink for queries like rb/sql-injection. \ No newline at end of file +- Calls to `ActiveRecord::Relation#annotate` are now recognized as`SqlExecution`s so that it will be considered as a sink for queries like rb/sql-injection. \ No newline at end of file From 4c53c341f6c2fb9022b18c69c6ed6655b0a8669f Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Thu, 14 Jul 2022 05:51:45 +0200 Subject: [PATCH 240/505] Swift: make `TargetFile::good()` a class invariant Fallible initialization has been moved to a factory function, and `commit` has been moved to the destructor. --- swift/extractor/SwiftExtractor.cpp | 35 +++++++++---------- swift/extractor/infra/TargetFile.cpp | 50 ++++++++++++++++++++-------- swift/extractor/infra/TargetFile.h | 25 ++++++++------ 3 files changed, 69 insertions(+), 41 deletions(-) diff --git a/swift/extractor/SwiftExtractor.cpp b/swift/extractor/SwiftExtractor.cpp index 88ad2152473..b36db1ba23b 100644 --- a/swift/extractor/SwiftExtractor.cpp +++ b/swift/extractor/SwiftExtractor.cpp @@ -77,6 +77,20 @@ static llvm::SmallVector getTopLevelDecls(swift::ModuleDecl& modul return ret; } +static void dumpArgs(TargetFile& out, const SwiftExtractorConfiguration& config) { + out << "/* extractor-args:\n"; + for (const auto& opt : config.frontendOptions) { + out << " " << std::quoted(opt) << " \\\n"; + } + out << "\n*/\n"; + + out << "/* swift-frontend-args:\n"; + for (const auto& opt : config.patchedFrontendOptions) { + out << " " << std::quoted(opt) << " \\\n"; + } + out << "\n*/\n"; +} + static void extractDeclarations(const SwiftExtractorConfiguration& config, swift::CompilerInstance& compiler, swift::ModuleDecl& module, @@ -86,25 +100,13 @@ static void extractDeclarations(const SwiftExtractorConfiguration& config, // The extractor can be called several times from different processes with // the same input file(s). Using `TargetFile` the first process will win, and the following // will just skip the work - TargetFile trapStream{filename + ".trap", config.trapDir, config.getTempTrapDir()}; - if (!trapStream.good()) { + auto trapTarget = TargetFile::create(filename + ".trap", config.trapDir, config.getTempTrapDir()); + if (!trapTarget) { // another process arrived first, nothing to do for us return; } - - trapStream << "/* extractor-args:\n"; - for (const auto& opt : config.frontendOptions) { - trapStream << " " << std::quoted(opt) << " \\\n"; - } - trapStream << "\n*/\n"; - - trapStream << "/* swift-frontend-args:\n"; - for (const auto& opt : config.patchedFrontendOptions) { - trapStream << " " << std::quoted(opt) << " \\\n"; - } - trapStream << "\n*/\n"; - - TrapDomain trap{trapStream}; + dumpArgs(*trapTarget, config); + TrapDomain trap{*trapTarget}; // TODO: move default location emission elsewhere, possibly in a separate global trap file // the following cannot conflict with actual files as those have an absolute path starting with / @@ -129,7 +131,6 @@ static void extractDeclarations(const SwiftExtractorConfiguration& config, auto fileLabel = trap.createLabel(name.str().str()); trap.emit(FilesTrap{fileLabel, name.str().str()}); } - trapStream.commit(); } static std::unordered_set collectInputFilenames(swift::CompilerInstance& compiler) { diff --git a/swift/extractor/infra/TargetFile.cpp b/swift/extractor/infra/TargetFile.cpp index 5051c0c191f..d6c4df74318 100644 --- a/swift/extractor/infra/TargetFile.cpp +++ b/swift/extractor/infra/TargetFile.cpp @@ -1,5 +1,10 @@ #include "swift/extractor/infra/TargetFile.h" +#include +#include +#include +#include + #include #include @@ -36,31 +41,49 @@ std::string initPath(std::string_view target, std::string_view dir) { TargetFile::TargetFile(std::string_view target, std::string_view targetDir, std::string_view workingDir) - : workingPath{initPath(target, workingDir)}, targetPath{initPath(target, targetDir)} { + : workingPath{initPath(target, workingDir)}, targetPath{initPath(target, targetDir)} {} + +bool TargetFile::init() { errno = 0; // since C++17 "x" mode opens with O_EXCL (fails if file already exists) if (auto f = std::fopen(targetPath.c_str(), "wx")) { std::fclose(f); out.open(workingPath); checkOutput("open file for writing"); - } else { - if (errno != EEXIST) { - error("open file for writing", targetPath); - } - // else we just lost the race, do nothing (good() will return false to signal this) + return true; } + if (errno != EEXIST) { + error("open file for writing", targetPath); + } + // else we just lost the race + return false; } -bool TargetFile::good() const { - return out && out.is_open(); +std::optional TargetFile::create(std::string_view target, + std::string_view targetDir, + std::string_view workingDir) { + TargetFile ret{target, targetDir, workingDir}; + if (ret.init()) return {std::move(ret)}; + return std::nullopt; +} + +TargetFile& TargetFile::operator=(TargetFile&& other) { + if (this != &other) { + commit(); + workingPath = std::move(other.workingPath); + targetPath = std::move(other.targetPath); + out = std::move(other.out); + } + return *this; } void TargetFile::commit() { - assert(good()); - out.close(); - errno = 0; - if (std::rename(workingPath.c_str(), targetPath.c_str()) != 0) { - error("rename file", targetPath); + if (out.is_open()) { + out.close(); + errno = 0; + if (std::rename(workingPath.c_str(), targetPath.c_str()) != 0) { + error("rename file", targetPath); + } } } @@ -69,5 +92,4 @@ void TargetFile::checkOutput(const char* action) { error(action, workingPath); } } - } // namespace codeql diff --git a/swift/extractor/infra/TargetFile.h b/swift/extractor/infra/TargetFile.h index 91a6f1651f1..551abe0bb76 100644 --- a/swift/extractor/infra/TargetFile.h +++ b/swift/extractor/infra/TargetFile.h @@ -2,28 +2,29 @@ #include #include -#include -#include +#include #include -#include -#include namespace codeql { -// Only the first process trying to open an `TargetFile` for a given `target` is allowed to do -// so, all others will have an instance with `good() == false` and failing on any other operation. +// Only the first process trying to create a `TargetFile` for a given `target` is allowed to do +// so, all others will have `create` return `std::nullopt`. // The content streamed to the `TargetFile` is written to `workingDir/target`, and is moved onto -// `targetDir/target` only when `commit()` is called +// `targetDir/target` on destruction. class TargetFile { std::string workingPath; std::string targetPath; std::ofstream out; public: - TargetFile(std::string_view target, std::string_view targetDir, std::string_view workingDir); + static std::optional create(std::string_view target, + std::string_view targetDir, + std::string_view workingDir); - bool good() const; - void commit(); + ~TargetFile() { commit(); } + + TargetFile(TargetFile&& other) = default; + TargetFile& operator=(TargetFile&& other); template TargetFile& operator<<(T&& value) { @@ -34,7 +35,11 @@ class TargetFile { } private: + TargetFile(std::string_view target, std::string_view targetDir, std::string_view workingDir); + + bool init(); void checkOutput(const char* action); + void commit(); }; } // namespace codeql From d748cb483d1ea03d6786d62eaf5a2292696bdd4b Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Thu, 14 Jul 2022 06:10:12 +0200 Subject: [PATCH 241/505] Swift: include cleanup Fix a problem with `sstream` not being transitively included on macOS. --- swift/extractor/SwiftExtractor.cpp | 4 ---- swift/extractor/trap/TrapDomain.h | 2 ++ 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/swift/extractor/SwiftExtractor.cpp b/swift/extractor/SwiftExtractor.cpp index b36db1ba23b..60ac3436fa8 100644 --- a/swift/extractor/SwiftExtractor.cpp +++ b/swift/extractor/SwiftExtractor.cpp @@ -1,11 +1,7 @@ #include "SwiftExtractor.h" -#include -#include #include -#include #include -#include #include #include diff --git a/swift/extractor/trap/TrapDomain.h b/swift/extractor/trap/TrapDomain.h index 42aed53d499..c84a3910134 100644 --- a/swift/extractor/trap/TrapDomain.h +++ b/swift/extractor/trap/TrapDomain.h @@ -1,6 +1,8 @@ #pragma once #include +#include + #include "swift/extractor/trap/TrapLabel.h" #include "swift/extractor/infra/TargetFile.h" From f1144b96720355a7c33c3b1c000e8a95bdc880be Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Thu, 14 Jul 2022 06:18:51 +0200 Subject: [PATCH 242/505] Swift: small TypeRepr visit rewording --- swift/extractor/visitors/SwiftVisitor.h | 2 +- swift/extractor/visitors/TypeVisitor.cpp | 4 ++-- swift/extractor/visitors/TypeVisitor.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/swift/extractor/visitors/SwiftVisitor.h b/swift/extractor/visitors/SwiftVisitor.h index f9464eafa46..53a259b5584 100644 --- a/swift/extractor/visitors/SwiftVisitor.h +++ b/swift/extractor/visitors/SwiftVisitor.h @@ -27,7 +27,7 @@ class SwiftVisitor : private SwiftDispatcher { void visit(swift::Pattern* pattern) override { patternVisitor.visit(pattern); } void visit(swift::TypeBase* type) override { typeVisitor.visit(type); } void visit(swift::TypeRepr* typeRepr, swift::Type type) override { - typeVisitor.visit(*typeRepr, type); + emit(typeVisitor.translateTypeRepr(*typeRepr, type)); } DeclVisitor declVisitor{*this}; diff --git a/swift/extractor/visitors/TypeVisitor.cpp b/swift/extractor/visitors/TypeVisitor.cpp index 5a1896080e0..868ce1a0db7 100644 --- a/swift/extractor/visitors/TypeVisitor.cpp +++ b/swift/extractor/visitors/TypeVisitor.cpp @@ -8,10 +8,10 @@ void TypeVisitor::visit(swift::TypeBase* type) { dispatcher_.emit(TypesTrap{label, type->getString(), canonicalLabel}); } -void TypeVisitor::visit(const swift::TypeRepr& typeRepr, swift::Type type) { +codeql::TypeRepr TypeVisitor::translateTypeRepr(const swift::TypeRepr& typeRepr, swift::Type type) { auto entry = dispatcher_.createEntry(typeRepr); entry.type = dispatcher_.fetchLabel(type); - dispatcher_.emit(entry); + return entry; } void TypeVisitor::visitProtocolType(swift::ProtocolType* type) { diff --git a/swift/extractor/visitors/TypeVisitor.h b/swift/extractor/visitors/TypeVisitor.h index b2deab60369..4fcd8a489f9 100644 --- a/swift/extractor/visitors/TypeVisitor.h +++ b/swift/extractor/visitors/TypeVisitor.h @@ -9,7 +9,7 @@ class TypeVisitor : public TypeVisitorBase { using TypeVisitorBase::TypeVisitorBase; void visit(swift::TypeBase* type); - void visit(const swift::TypeRepr& typeRepr, swift::Type type); + codeql::TypeRepr translateTypeRepr(const swift::TypeRepr& typeRepr, swift::Type type); void visitProtocolType(swift::ProtocolType* type); void visitEnumType(swift::EnumType* type); From da8123072dba05681836df5908bb1c4d110a807d Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 14 Jul 2022 09:38:10 +0200 Subject: [PATCH 243/505] Apply suggestions from doc review Co-authored-by: mc <42146119+mchammer01@users.noreply.github.com> --- .../src/Security/CWE-178/CaseSensitiveMiddlewarePath.qhelp | 6 +++--- .../ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.ql | 2 +- .../change-notes/2022-06-27-case-sensitive-middleware.md | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.qhelp b/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.qhelp index 13e2331bc62..96bf1c18c94 100644 --- a/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.qhelp +++ b/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.qhelp @@ -7,21 +7,21 @@

    Using a case-sensitive regular expression path in a middleware route enables an attacker to bypass that middleware when accessing an endpoint with a case-insensitive path. -Paths specified using a string are case insensitive, whereas regular expressions are case sensitive by default. +Paths specified using a string are case-insensitive, whereas regular expressions are case-sensitive by default.

    When using a regular expression as a middleware path, make sure the regular expression is -case insensitive by adding the i flag. +case-insensitive by adding the i flag.

    The following example restricts access to paths in the /admin path to users logged in as -an administrator: +administrators:

    diff --git a/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.ql b/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.ql index df3beecfb13..ccf659bf024 100644 --- a/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.ql +++ b/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.ql @@ -1,6 +1,6 @@ /** * @name Case-sensitive middleware path - * @description Middleware with case-sensitive paths do not protect endpoints with case-insensitive paths + * @description Middleware with case-sensitive paths do not protect endpoints with case-insensitive paths. * @kind problem * @problem.severity warning * @security-severity 7.3 diff --git a/javascript/ql/src/change-notes/2022-06-27-case-sensitive-middleware.md b/javascript/ql/src/change-notes/2022-06-27-case-sensitive-middleware.md index 1593596e939..09895db1e2c 100644 --- a/javascript/ql/src/change-notes/2022-06-27-case-sensitive-middleware.md +++ b/javascript/ql/src/change-notes/2022-06-27-case-sensitive-middleware.md @@ -2,5 +2,5 @@ category: newQuery --- -- A new query "case sensitive middleware path" (`js/case-sensitive-middleware-path`) has been added. +- A new query "Case-sensitive middleware path" (`js/case-sensitive-middleware-path`) has been added. It highlights middleware routes that can be bypassed due to having a case-sensitive regular expression path. From ed80089d7ceffc38164a0a1cd45e1f4d36a3c99b Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 14 Jul 2022 09:45:44 +0200 Subject: [PATCH 244/505] fix some QL-for-QL warnings in JS --- .../modelbuilding/extraction/ExtractEndpointData.qll | 4 ++-- .../semmle/javascript/dataflow/internal/CallGraphs.qll | 2 +- .../dataflow/internal/PropertyTypeInference.qll | 4 ++-- .../frameworks/AngularJS/AngularJSExpressions.qll | 4 ++-- .../ql/lib/semmle/javascript/frameworks/Handlebars.qll | 10 +++++----- .../ql/src/Expressions/CompareIdenticalValues.ql | 2 +- .../ql/src/Security/CWE-200/PrivateFileExposure.ql | 2 +- ql/ql/src/queries/style/MissingParameterInQlDoc.ql | 2 +- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/extraction/ExtractEndpointData.qll b/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/extraction/ExtractEndpointData.qll index 07ee16fda50..53d559d2568 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/extraction/ExtractEndpointData.qll +++ b/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/extraction/ExtractEndpointData.qll @@ -188,10 +188,10 @@ module FlowFromSource { Query getQuery() { result = q } - /** The sinks are the endpoints we're extracting. */ + /** Holds if `sink` is an endpoint we're extracting. */ override predicate isSink(DataFlow::Node sink) { sink = getAnEndpoint(q) } - /** The sinks are the endpoints we're extracting. */ + /** Holds if `sink` is an endpoint we're extracting. */ override predicate isSink(DataFlow::Node sink, DataFlow::FlowLabel lbl) { sink = getAnEndpoint(q) and exists(lbl) } diff --git a/javascript/ql/lib/semmle/javascript/dataflow/internal/CallGraphs.qll b/javascript/ql/lib/semmle/javascript/dataflow/internal/CallGraphs.qll index c2ce840fae2..94a6532b107 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/internal/CallGraphs.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/internal/CallGraphs.qll @@ -190,7 +190,7 @@ module CallGraph { } /** - * Holds if `ref` installs an accessor on an object. Such property writes should not + * Holds if `write` installs an accessor on an object. Such property writes should not * be considered calls to an accessor. */ pragma[nomagic] diff --git a/javascript/ql/lib/semmle/javascript/dataflow/internal/PropertyTypeInference.qll b/javascript/ql/lib/semmle/javascript/dataflow/internal/PropertyTypeInference.qll index 0f6b816daeb..15a4fec7406 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/internal/PropertyTypeInference.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/internal/PropertyTypeInference.qll @@ -10,7 +10,7 @@ private import AbstractPropertiesImpl private import AbstractValuesImpl /** - * Flow analysis for property reads, either explicitly (`x.p` or `x[e]`) or + * An analyzed property read, either explicitly (`x.p` or `x[e]`) or * implicitly. */ abstract class AnalyzedPropertyRead extends DataFlow::AnalyzedNode { @@ -86,7 +86,7 @@ pragma[noinline] private predicate isTrackedPropertyName(string prop) { exists(MkAbstractProperty(_, prop)) } /** - * Flow analysis for property writes, including exports (which are + * An analyzed property write, including exports (which are * modeled as assignments to `module.exports`). */ abstract class AnalyzedPropertyWrite extends DataFlow::Node { diff --git a/javascript/ql/lib/semmle/javascript/frameworks/AngularJS/AngularJSExpressions.qll b/javascript/ql/lib/semmle/javascript/frameworks/AngularJS/AngularJSExpressions.qll index 050c123e30a..d379184c11f 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/AngularJS/AngularJSExpressions.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/AngularJS/AngularJSExpressions.qll @@ -92,10 +92,10 @@ abstract private class HtmlAttributeAsNgSourceProvider extends NgSourceProvider, endColumn = startColumn + src.length() - 1 } - /** The source code of the expression. */ + /** Gets the source code of the expression. */ abstract string getSource(); - /** The offset into the attribute where the expression starts. */ + /** Gets the offset into the attribute where the expression starts. */ abstract int getOffset(); override DOM::ElementDefinition getEnclosingElement() { result = this.getElement() } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Handlebars.qll b/javascript/ql/lib/semmle/javascript/frameworks/Handlebars.qll index 372e0145003..2e376f49ae7 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Handlebars.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Handlebars.qll @@ -61,13 +61,13 @@ private module HandlebarsTaintSteps { * the `FunctionNode` representing `function loudHelper`, and return its parameter `text`. */ private DataFlow::ParameterNode getRegisteredHelperParam( - string helperName, DataFlow::FunctionNode helperFunction, int paramIndex + string helperName, DataFlow::FunctionNode func, int paramIndex ) { exists(DataFlow::CallNode registerHelperCall | registerHelperCall = any(Handlebars::Handlebars hb).getAMemberCall("registerHelper") and registerHelperCall.getArgument(0).mayHaveStringValue(helperName) and - helperFunction = registerHelperCall.getArgument(1).getAFunctionValue() and - result = helperFunction.getParameter(paramIndex) + func = registerHelperCall.getArgument(1).getAFunctionValue() and + result = func.getParameter(paramIndex) ) } @@ -132,7 +132,7 @@ private module HandlebarsTaintSteps { private predicate isHandlebarsArgStep(DataFlow::Node pred, DataFlow::Node succ) { exists( string helperName, DataFlow::CallNode templatingCall, DataFlow::CallNode compileCall, - DataFlow::FunctionNode helperFunction + DataFlow::FunctionNode func | templatingCall = compiledTemplate(compileCall).getACall() and exists(string templateText, string paramName, int argIdx | @@ -140,7 +140,7 @@ private module HandlebarsTaintSteps { | pred = templatingCall.getArgument(0).getALocalSource().getAPropertyWrite(paramName).getRhs() and isTemplateHelperCallArg(templateText, helperName, argIdx, paramName) and - succ = getRegisteredHelperParam(helperName, helperFunction, argIdx) + succ = getRegisteredHelperParam(helperName, func, argIdx) ) ) } diff --git a/javascript/ql/src/Expressions/CompareIdenticalValues.ql b/javascript/ql/src/Expressions/CompareIdenticalValues.ql index 48eae0c49cd..19e9338fb6f 100644 --- a/javascript/ql/src/Expressions/CompareIdenticalValues.ql +++ b/javascript/ql/src/Expressions/CompareIdenticalValues.ql @@ -38,7 +38,7 @@ predicate accessWithConversions(Expr e, Variable v) { } /** - * A comment containing the word "NaN". + * Holds if `c` is a comment containing the word "NaN". */ predicate isNaNComment(Comment c, string filePath, int startLine) { c.getText().matches("%NaN%") and diff --git a/javascript/ql/src/Security/CWE-200/PrivateFileExposure.ql b/javascript/ql/src/Security/CWE-200/PrivateFileExposure.ql index add8679dee7..73a20ce2249 100644 --- a/javascript/ql/src/Security/CWE-200/PrivateFileExposure.ql +++ b/javascript/ql/src/Security/CWE-200/PrivateFileExposure.ql @@ -76,7 +76,7 @@ Folder getAPackageJsonFolder() { result = any(PackageJson json).getFile().getPar * the current working folder, or the root folder. * All of these might cause information to be leaked. * - * For the first case it is assumed that the presence of a `package.json` file means that a `node_modules` folder can also exist. + * For the first case it is assumed that the presence of a `package.json` file means that a "node_modules" folder can also exist. * * For the root/home/working folder, they contain so much information that they must leak information somehow (e.g. ssh keys in the `~/.ssh` folder). */ diff --git a/ql/ql/src/queries/style/MissingParameterInQlDoc.ql b/ql/ql/src/queries/style/MissingParameterInQlDoc.ql index c2b1e80fe53..e0ac524e7ee 100644 --- a/ql/ql/src/queries/style/MissingParameterInQlDoc.ql +++ b/ql/ql/src/queries/style/MissingParameterInQlDoc.ql @@ -46,7 +46,7 @@ private string getAMentionedNonParameter(Predicate p) { not result = [ "true", "false", "NaN", "this", "forall", "exists", "null", "break", "return", "not", "if", - "then", "else", "import" + "then", "else", "import", "async" ] and not result = any(Aggregate a).getKind() and // min, max, sum, count, etc. not result = getMentionedThings(p.getLocation().getFile()) and From d1aa0d7dd38cc4c9a8c0942a7e57bb367bd7ffc4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 14 Jul 2022 08:56:03 +0000 Subject: [PATCH 245/505] Release preparation for version 2.10.1 --- cpp/ql/lib/CHANGELOG.md | 6 ++++++ .../0.3.1.md} | 7 ++++--- cpp/ql/lib/codeql-pack.release.yml | 2 +- cpp/ql/lib/qlpack.yml | 2 +- cpp/ql/src/CHANGELOG.md | 6 ++++++ .../0.3.0.md} | 7 ++++--- cpp/ql/src/codeql-pack.release.yml | 2 +- cpp/ql/src/qlpack.yml | 2 +- csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md | 2 ++ .../Solorigate/lib/change-notes/released/1.2.1.md | 1 + .../Solorigate/lib/codeql-pack.release.yml | 2 +- csharp/ql/campaigns/Solorigate/lib/qlpack.yml | 2 +- csharp/ql/campaigns/Solorigate/src/CHANGELOG.md | 2 ++ .../Solorigate/src/change-notes/released/1.2.1.md | 1 + .../Solorigate/src/codeql-pack.release.yml | 2 +- csharp/ql/campaigns/Solorigate/src/qlpack.yml | 2 +- csharp/ql/lib/CHANGELOG.md | 2 ++ csharp/ql/lib/change-notes/released/0.3.1.md | 1 + csharp/ql/lib/codeql-pack.release.yml | 2 +- csharp/ql/lib/qlpack.yml | 2 +- csharp/ql/src/CHANGELOG.md | 6 ++++++ .../0.3.0.md} | 7 ++++--- csharp/ql/src/codeql-pack.release.yml | 2 +- csharp/ql/src/qlpack.yml | 2 +- go/ql/lib/CHANGELOG.md | 2 ++ go/ql/lib/change-notes/released/0.2.1.md | 1 + go/ql/lib/codeql-pack.release.yml | 2 +- go/ql/lib/qlpack.yml | 2 +- go/ql/src/CHANGELOG.md | 2 ++ go/ql/src/change-notes/released/0.2.1.md | 1 + go/ql/src/codeql-pack.release.yml | 2 +- go/ql/src/qlpack.yml | 2 +- java/ql/lib/CHANGELOG.md | 13 +++++++++++++ .../2022-05-18-android-external-storage.md | 4 ---- .../change-notes/2022-06-13-kotlin-break-loops.md | 4 ---- java/ql/lib/change-notes/2022-06-27-isInline.md | 4 ---- java/ql/lib/change-notes/2022-07-12-errortype.md | 4 ---- java/ql/lib/change-notes/2022-07-13-properites.md | 4 ---- java/ql/lib/change-notes/released/0.3.1.md | 12 ++++++++++++ java/ql/lib/codeql-pack.release.yml | 2 +- java/ql/lib/qlpack.yml | 2 +- java/ql/src/CHANGELOG.md | 12 ++++++++++++ .../2022-06-29-move-contextual-queries.md | 4 ---- .../0.3.0.md} | 13 +++++++++---- java/ql/src/codeql-pack.release.yml | 2 +- java/ql/src/qlpack.yml | 2 +- javascript/ql/lib/CHANGELOG.md | 8 ++++++++ .../2022-06-22-sensitive-common-words copy.md | 4 ---- .../2022-06-22-sensitive-common-words.md | 4 ---- .../ql/lib/change-notes/2022-06-30-chownr.md | 4 ---- javascript/ql/lib/change-notes/released/0.2.1.md | 7 +++++++ javascript/ql/lib/codeql-pack.release.yml | 2 +- javascript/ql/lib/qlpack.yml | 2 +- javascript/ql/src/CHANGELOG.md | 6 ++++++ .../0.3.0.md} | 7 ++++--- javascript/ql/src/codeql-pack.release.yml | 2 +- javascript/ql/src/qlpack.yml | 2 +- python/ql/lib/CHANGELOG.md | 15 +++++++++++++++ .../2022-06-22-sensitive-common-words.md | 4 ---- .../0.5.1.md} | 10 +++++++--- python/ql/lib/codeql-pack.release.yml | 2 +- python/ql/lib/qlpack.yml | 2 +- python/ql/src/CHANGELOG.md | 6 ++++++ .../0.3.0.md} | 7 ++++--- python/ql/src/codeql-pack.release.yml | 2 +- python/ql/src/qlpack.yml | 2 +- ruby/ql/lib/CHANGELOG.md | 9 +++++++++ .../2022-06-22-sensitive-common-words.md | 4 ---- .../0.3.1.md} | 8 +++++--- ruby/ql/lib/codeql-pack.release.yml | 2 +- ruby/ql/lib/qlpack.yml | 2 +- ruby/ql/src/CHANGELOG.md | 6 ++++++ .../0.3.0.md} | 7 ++++--- ruby/ql/src/codeql-pack.release.yml | 2 +- ruby/ql/src/qlpack.yml | 2 +- 75 files changed, 204 insertions(+), 104 deletions(-) rename cpp/ql/lib/change-notes/{2022-07-12-cover-more-nullness-cases.md => released/0.3.1.md} (81%) rename cpp/ql/src/change-notes/{2022-06-29-move-contextual-queries.md => released/0.3.0.md} (77%) create mode 100644 csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.2.1.md create mode 100644 csharp/ql/campaigns/Solorigate/src/change-notes/released/1.2.1.md create mode 100644 csharp/ql/lib/change-notes/released/0.3.1.md rename csharp/ql/src/change-notes/{2022-06-29-move-contextual-queries.md => released/0.3.0.md} (77%) create mode 100644 go/ql/lib/change-notes/released/0.2.1.md create mode 100644 go/ql/src/change-notes/released/0.2.1.md delete mode 100644 java/ql/lib/change-notes/2022-05-18-android-external-storage.md delete mode 100644 java/ql/lib/change-notes/2022-06-13-kotlin-break-loops.md delete mode 100644 java/ql/lib/change-notes/2022-06-27-isInline.md delete mode 100644 java/ql/lib/change-notes/2022-07-12-errortype.md delete mode 100644 java/ql/lib/change-notes/2022-07-13-properites.md create mode 100644 java/ql/lib/change-notes/released/0.3.1.md delete mode 100644 java/ql/src/change-notes/2022-06-29-move-contextual-queries.md rename java/ql/src/change-notes/{2022-04-27-intent-verification.md => released/0.3.0.md} (57%) delete mode 100644 javascript/ql/lib/change-notes/2022-06-22-sensitive-common-words copy.md delete mode 100644 javascript/ql/lib/change-notes/2022-06-22-sensitive-common-words.md delete mode 100644 javascript/ql/lib/change-notes/2022-06-30-chownr.md create mode 100644 javascript/ql/lib/change-notes/released/0.2.1.md rename javascript/ql/src/change-notes/{2022-06-29-move-contextual-queries.md => released/0.3.0.md} (78%) delete mode 100644 python/ql/lib/change-notes/2022-06-22-sensitive-common-words.md rename python/ql/lib/change-notes/{2022-06-28-api-graph-api.md => released/0.5.1.md} (58%) rename python/ql/src/change-notes/{2022-06-29-move-contextual-queries.md => released/0.3.0.md} (77%) delete mode 100644 ruby/ql/lib/change-notes/2022-06-22-sensitive-common-words.md rename ruby/ql/lib/change-notes/{2022-07-11-command-execution.md => released/0.3.1.md} (65%) rename ruby/ql/src/change-notes/{2022-06-29-move-contextual-queries.md => released/0.3.0.md} (77%) diff --git a/cpp/ql/lib/CHANGELOG.md b/cpp/ql/lib/CHANGELOG.md index c1cefbed8f9..3e3f0b4531b 100644 --- a/cpp/ql/lib/CHANGELOG.md +++ b/cpp/ql/lib/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.3.1 + +### Minor Analysis Improvements + +* `AnalysedExpr::isNullCheck` and `AnalysedExpr::isValidCheck` have been updated to handle variable accesses on the left-hand side of the the C++ logical and variable declarations in conditions. + ## 0.3.0 ### Deprecated APIs diff --git a/cpp/ql/lib/change-notes/2022-07-12-cover-more-nullness-cases.md b/cpp/ql/lib/change-notes/released/0.3.1.md similarity index 81% rename from cpp/ql/lib/change-notes/2022-07-12-cover-more-nullness-cases.md rename to cpp/ql/lib/change-notes/released/0.3.1.md index eef564991f5..4b9a3206c96 100644 --- a/cpp/ql/lib/change-notes/2022-07-12-cover-more-nullness-cases.md +++ b/cpp/ql/lib/change-notes/released/0.3.1.md @@ -1,4 +1,5 @@ ---- -category: minorAnalysis ---- +## 0.3.1 + +### Minor Analysis Improvements + * `AnalysedExpr::isNullCheck` and `AnalysedExpr::isValidCheck` have been updated to handle variable accesses on the left-hand side of the the C++ logical and variable declarations in conditions. diff --git a/cpp/ql/lib/codeql-pack.release.yml b/cpp/ql/lib/codeql-pack.release.yml index 95f6e3a0ba6..bb106b1cb63 100644 --- a/cpp/ql/lib/codeql-pack.release.yml +++ b/cpp/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.3.0 +lastReleaseVersion: 0.3.1 diff --git a/cpp/ql/lib/qlpack.yml b/cpp/ql/lib/qlpack.yml index a20077271d7..ae668b219fb 100644 --- a/cpp/ql/lib/qlpack.yml +++ b/cpp/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/cpp-all -version: 0.3.1-dev +version: 0.3.1 groups: cpp dbscheme: semmlecode.cpp.dbscheme extractor: cpp diff --git a/cpp/ql/src/CHANGELOG.md b/cpp/ql/src/CHANGELOG.md index 2b404ff5288..e87fc5dce39 100644 --- a/cpp/ql/src/CHANGELOG.md +++ b/cpp/ql/src/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.3.0 + +### Breaking Changes + +* Contextual queries and the query libraries they depend on have been moved to the `codeql/cpp-all` package. + ## 0.2.0 ## 0.1.4 diff --git a/cpp/ql/src/change-notes/2022-06-29-move-contextual-queries.md b/cpp/ql/src/change-notes/released/0.3.0.md similarity index 77% rename from cpp/ql/src/change-notes/2022-06-29-move-contextual-queries.md rename to cpp/ql/src/change-notes/released/0.3.0.md index cc5464d58b3..75d99f333c9 100644 --- a/cpp/ql/src/change-notes/2022-06-29-move-contextual-queries.md +++ b/cpp/ql/src/change-notes/released/0.3.0.md @@ -1,4 +1,5 @@ ---- -category: breaking ---- +## 0.3.0 + +### Breaking Changes + * Contextual queries and the query libraries they depend on have been moved to the `codeql/cpp-all` package. diff --git a/cpp/ql/src/codeql-pack.release.yml b/cpp/ql/src/codeql-pack.release.yml index 5274e27ed52..95f6e3a0ba6 100644 --- a/cpp/ql/src/codeql-pack.release.yml +++ b/cpp/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.2.0 +lastReleaseVersion: 0.3.0 diff --git a/cpp/ql/src/qlpack.yml b/cpp/ql/src/qlpack.yml index 62cac967801..1a97e24c9c7 100644 --- a/cpp/ql/src/qlpack.yml +++ b/cpp/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/cpp-queries -version: 0.2.1-dev +version: 0.3.0 groups: - cpp - queries diff --git a/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md b/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md index 30c583ee913..de0a7eeae4b 100644 --- a/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md +++ b/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md @@ -1,3 +1,5 @@ +## 1.2.1 + ## 1.2.0 ## 1.1.4 diff --git a/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.2.1.md b/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.2.1.md new file mode 100644 index 00000000000..ddf8866b672 --- /dev/null +++ b/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.2.1.md @@ -0,0 +1 @@ +## 1.2.1 diff --git a/csharp/ql/campaigns/Solorigate/lib/codeql-pack.release.yml b/csharp/ql/campaigns/Solorigate/lib/codeql-pack.release.yml index 75430e73d1c..73dd403938c 100644 --- a/csharp/ql/campaigns/Solorigate/lib/codeql-pack.release.yml +++ b/csharp/ql/campaigns/Solorigate/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 1.2.0 +lastReleaseVersion: 1.2.1 diff --git a/csharp/ql/campaigns/Solorigate/lib/qlpack.yml b/csharp/ql/campaigns/Solorigate/lib/qlpack.yml index bc7eaf4142c..3b56b5ac98e 100644 --- a/csharp/ql/campaigns/Solorigate/lib/qlpack.yml +++ b/csharp/ql/campaigns/Solorigate/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/csharp-solorigate-all -version: 1.2.1-dev +version: 1.2.1 groups: - csharp - solorigate diff --git a/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md b/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md index 30c583ee913..de0a7eeae4b 100644 --- a/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md +++ b/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md @@ -1,3 +1,5 @@ +## 1.2.1 + ## 1.2.0 ## 1.1.4 diff --git a/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.2.1.md b/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.2.1.md new file mode 100644 index 00000000000..ddf8866b672 --- /dev/null +++ b/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.2.1.md @@ -0,0 +1 @@ +## 1.2.1 diff --git a/csharp/ql/campaigns/Solorigate/src/codeql-pack.release.yml b/csharp/ql/campaigns/Solorigate/src/codeql-pack.release.yml index 75430e73d1c..73dd403938c 100644 --- a/csharp/ql/campaigns/Solorigate/src/codeql-pack.release.yml +++ b/csharp/ql/campaigns/Solorigate/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 1.2.0 +lastReleaseVersion: 1.2.1 diff --git a/csharp/ql/campaigns/Solorigate/src/qlpack.yml b/csharp/ql/campaigns/Solorigate/src/qlpack.yml index 00725e23666..54326ca55ad 100644 --- a/csharp/ql/campaigns/Solorigate/src/qlpack.yml +++ b/csharp/ql/campaigns/Solorigate/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/csharp-solorigate-queries -version: 1.2.1-dev +version: 1.2.1 groups: - csharp - solorigate diff --git a/csharp/ql/lib/CHANGELOG.md b/csharp/ql/lib/CHANGELOG.md index 3f49fe5ade3..d1c89626798 100644 --- a/csharp/ql/lib/CHANGELOG.md +++ b/csharp/ql/lib/CHANGELOG.md @@ -1,3 +1,5 @@ +## 0.3.1 + ## 0.3.0 ### Deprecated APIs diff --git a/csharp/ql/lib/change-notes/released/0.3.1.md b/csharp/ql/lib/change-notes/released/0.3.1.md new file mode 100644 index 00000000000..2b0719929a1 --- /dev/null +++ b/csharp/ql/lib/change-notes/released/0.3.1.md @@ -0,0 +1 @@ +## 0.3.1 diff --git a/csharp/ql/lib/codeql-pack.release.yml b/csharp/ql/lib/codeql-pack.release.yml index 95f6e3a0ba6..bb106b1cb63 100644 --- a/csharp/ql/lib/codeql-pack.release.yml +++ b/csharp/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.3.0 +lastReleaseVersion: 0.3.1 diff --git a/csharp/ql/lib/qlpack.yml b/csharp/ql/lib/qlpack.yml index 3f371c01e92..6aff1d2ac68 100644 --- a/csharp/ql/lib/qlpack.yml +++ b/csharp/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/csharp-all -version: 0.3.1-dev +version: 0.3.1 groups: csharp dbscheme: semmlecode.csharp.dbscheme extractor: csharp diff --git a/csharp/ql/src/CHANGELOG.md b/csharp/ql/src/CHANGELOG.md index e7ce0b0b471..bf9e8f9c41f 100644 --- a/csharp/ql/src/CHANGELOG.md +++ b/csharp/ql/src/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.3.0 + +### Breaking Changes + +* Contextual queries and the query libraries they depend on have been moved to the `codeql/csharp-all` package. + ## 0.2.0 ### Query Metadata Changes diff --git a/csharp/ql/src/change-notes/2022-06-29-move-contextual-queries.md b/csharp/ql/src/change-notes/released/0.3.0.md similarity index 77% rename from csharp/ql/src/change-notes/2022-06-29-move-contextual-queries.md rename to csharp/ql/src/change-notes/released/0.3.0.md index a27c68766c0..001d034c251 100644 --- a/csharp/ql/src/change-notes/2022-06-29-move-contextual-queries.md +++ b/csharp/ql/src/change-notes/released/0.3.0.md @@ -1,4 +1,5 @@ ---- -category: breaking ---- +## 0.3.0 + +### Breaking Changes + * Contextual queries and the query libraries they depend on have been moved to the `codeql/csharp-all` package. diff --git a/csharp/ql/src/codeql-pack.release.yml b/csharp/ql/src/codeql-pack.release.yml index 5274e27ed52..95f6e3a0ba6 100644 --- a/csharp/ql/src/codeql-pack.release.yml +++ b/csharp/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.2.0 +lastReleaseVersion: 0.3.0 diff --git a/csharp/ql/src/qlpack.yml b/csharp/ql/src/qlpack.yml index 1002c6a56ad..b4e5b89773f 100644 --- a/csharp/ql/src/qlpack.yml +++ b/csharp/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/csharp-queries -version: 0.2.1-dev +version: 0.3.0 groups: - csharp - queries diff --git a/go/ql/lib/CHANGELOG.md b/go/ql/lib/CHANGELOG.md index 112f4fab585..23c4fc2eb4f 100644 --- a/go/ql/lib/CHANGELOG.md +++ b/go/ql/lib/CHANGELOG.md @@ -1,3 +1,5 @@ +## 0.2.1 + ## 0.2.0 ### Deprecated APIs diff --git a/go/ql/lib/change-notes/released/0.2.1.md b/go/ql/lib/change-notes/released/0.2.1.md new file mode 100644 index 00000000000..c260de2a9ee --- /dev/null +++ b/go/ql/lib/change-notes/released/0.2.1.md @@ -0,0 +1 @@ +## 0.2.1 diff --git a/go/ql/lib/codeql-pack.release.yml b/go/ql/lib/codeql-pack.release.yml index 5274e27ed52..df29a726bcc 100644 --- a/go/ql/lib/codeql-pack.release.yml +++ b/go/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.2.0 +lastReleaseVersion: 0.2.1 diff --git a/go/ql/lib/qlpack.yml b/go/ql/lib/qlpack.yml index 964f5d4dd13..8f46c7040bb 100644 --- a/go/ql/lib/qlpack.yml +++ b/go/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/go-all -version: 0.2.1-dev +version: 0.2.1 groups: go dbscheme: go.dbscheme extractor: go diff --git a/go/ql/src/CHANGELOG.md b/go/ql/src/CHANGELOG.md index bed2509f5d3..1697aa9e561 100644 --- a/go/ql/src/CHANGELOG.md +++ b/go/ql/src/CHANGELOG.md @@ -1,3 +1,5 @@ +## 0.2.1 + ## 0.2.0 ## 0.1.4 diff --git a/go/ql/src/change-notes/released/0.2.1.md b/go/ql/src/change-notes/released/0.2.1.md new file mode 100644 index 00000000000..c260de2a9ee --- /dev/null +++ b/go/ql/src/change-notes/released/0.2.1.md @@ -0,0 +1 @@ +## 0.2.1 diff --git a/go/ql/src/codeql-pack.release.yml b/go/ql/src/codeql-pack.release.yml index 5274e27ed52..df29a726bcc 100644 --- a/go/ql/src/codeql-pack.release.yml +++ b/go/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.2.0 +lastReleaseVersion: 0.2.1 diff --git a/go/ql/src/qlpack.yml b/go/ql/src/qlpack.yml index 80a4430b8da..3aff018b452 100644 --- a/go/ql/src/qlpack.yml +++ b/go/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/go-queries -version: 0.2.1-dev +version: 0.2.1 groups: - go - queries diff --git a/java/ql/lib/CHANGELOG.md b/java/ql/lib/CHANGELOG.md index 41b23a74d1f..6b5e4ec9f09 100644 --- a/java/ql/lib/CHANGELOG.md +++ b/java/ql/lib/CHANGELOG.md @@ -1,3 +1,16 @@ +## 0.3.1 + +### New Features + +* Added an `ErrorType` class. An instance of this class will be used if an extractor is unable to extract a type, or if an up/downgrade script is unable to provide a type. + +### Minor Analysis Improvements + +* Added data-flow models for `java.util.Properites`. Additional results may be found where relevant data is stored in and then retrieved from a `Properties` instance. +Added `Modifier.isInline()`. +* Removed Kotlin-specific database and QL structures for loops and `break`/`continue` statements. The Kotlin extractor was changed to reuse the Java structures for these constructs. +Added additional flow sources for uses of external storage on Android. + ## 0.3.0 ### Deprecated APIs diff --git a/java/ql/lib/change-notes/2022-05-18-android-external-storage.md b/java/ql/lib/change-notes/2022-05-18-android-external-storage.md deleted file mode 100644 index b3d5fa793b3..00000000000 --- a/java/ql/lib/change-notes/2022-05-18-android-external-storage.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -Added additional flow sources for uses of external storage on Android. \ No newline at end of file diff --git a/java/ql/lib/change-notes/2022-06-13-kotlin-break-loops.md b/java/ql/lib/change-notes/2022-06-13-kotlin-break-loops.md deleted file mode 100644 index 412cf7d5ff5..00000000000 --- a/java/ql/lib/change-notes/2022-06-13-kotlin-break-loops.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* Removed Kotlin-specific database and QL structures for loops and `break`/`continue` statements. The Kotlin extractor was changed to reuse the Java structures for these constructs. diff --git a/java/ql/lib/change-notes/2022-06-27-isInline.md b/java/ql/lib/change-notes/2022-06-27-isInline.md deleted file mode 100644 index ad73ed8bf82..00000000000 --- a/java/ql/lib/change-notes/2022-06-27-isInline.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -Added `Modifier.isInline()`. diff --git a/java/ql/lib/change-notes/2022-07-12-errortype.md b/java/ql/lib/change-notes/2022-07-12-errortype.md deleted file mode 100644 index 97f851bb936..00000000000 --- a/java/ql/lib/change-notes/2022-07-12-errortype.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: feature ---- -* Added an `ErrorType` class. An instance of this class will be used if an extractor is unable to extract a type, or if an up/downgrade script is unable to provide a type. diff --git a/java/ql/lib/change-notes/2022-07-13-properites.md b/java/ql/lib/change-notes/2022-07-13-properites.md deleted file mode 100644 index 2be74455704..00000000000 --- a/java/ql/lib/change-notes/2022-07-13-properites.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* Added data-flow models for `java.util.Properites`. Additional results may be found where relevant data is stored in and then retrieved from a `Properties` instance. diff --git a/java/ql/lib/change-notes/released/0.3.1.md b/java/ql/lib/change-notes/released/0.3.1.md new file mode 100644 index 00000000000..8c0e3158b0b --- /dev/null +++ b/java/ql/lib/change-notes/released/0.3.1.md @@ -0,0 +1,12 @@ +## 0.3.1 + +### New Features + +* Added an `ErrorType` class. An instance of this class will be used if an extractor is unable to extract a type, or if an up/downgrade script is unable to provide a type. + +### Minor Analysis Improvements + +* Added data-flow models for `java.util.Properites`. Additional results may be found where relevant data is stored in and then retrieved from a `Properties` instance. +Added `Modifier.isInline()`. +* Removed Kotlin-specific database and QL structures for loops and `break`/`continue` statements. The Kotlin extractor was changed to reuse the Java structures for these constructs. +Added additional flow sources for uses of external storage on Android. diff --git a/java/ql/lib/codeql-pack.release.yml b/java/ql/lib/codeql-pack.release.yml index 95f6e3a0ba6..bb106b1cb63 100644 --- a/java/ql/lib/codeql-pack.release.yml +++ b/java/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.3.0 +lastReleaseVersion: 0.3.1 diff --git a/java/ql/lib/qlpack.yml b/java/ql/lib/qlpack.yml index 541fad197e0..29b02e16b6e 100644 --- a/java/ql/lib/qlpack.yml +++ b/java/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/java-all -version: 0.3.1-dev +version: 0.3.1 groups: java dbscheme: config/semmlecode.dbscheme extractor: java diff --git a/java/ql/src/CHANGELOG.md b/java/ql/src/CHANGELOG.md index 1f8a00fb1ff..fc9988443b2 100644 --- a/java/ql/src/CHANGELOG.md +++ b/java/ql/src/CHANGELOG.md @@ -1,3 +1,15 @@ +## 0.3.0 + +### Breaking Changes + +* Contextual queries and the query libraries they depend on have been moved to the `codeql/java-all` package. + +### New Queries + +* A new query "Improper verification of intent by broadcast receiver" (`java/improper-intent-verification`) has been added. +This query finds instances of Android `BroadcastReceiver`s that don't verify the action string of received intents when registered +to receive system intents. + ## 0.2.0 ### Minor Analysis Improvements diff --git a/java/ql/src/change-notes/2022-06-29-move-contextual-queries.md b/java/ql/src/change-notes/2022-06-29-move-contextual-queries.md deleted file mode 100644 index 02ff5b6d59c..00000000000 --- a/java/ql/src/change-notes/2022-06-29-move-contextual-queries.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: breaking ---- -* Contextual queries and the query libraries they depend on have been moved to the `codeql/java-all` package. diff --git a/java/ql/src/change-notes/2022-04-27-intent-verification.md b/java/ql/src/change-notes/released/0.3.0.md similarity index 57% rename from java/ql/src/change-notes/2022-04-27-intent-verification.md rename to java/ql/src/change-notes/released/0.3.0.md index e5e0e287753..48b478ec905 100644 --- a/java/ql/src/change-notes/2022-04-27-intent-verification.md +++ b/java/ql/src/change-notes/released/0.3.0.md @@ -1,6 +1,11 @@ ---- -category: newQuery ---- +## 0.3.0 + +### Breaking Changes + +* Contextual queries and the query libraries they depend on have been moved to the `codeql/java-all` package. + +### New Queries + * A new query "Improper verification of intent by broadcast receiver" (`java/improper-intent-verification`) has been added. This query finds instances of Android `BroadcastReceiver`s that don't verify the action string of received intents when registered -to receive system intents. \ No newline at end of file +to receive system intents. diff --git a/java/ql/src/codeql-pack.release.yml b/java/ql/src/codeql-pack.release.yml index 5274e27ed52..95f6e3a0ba6 100644 --- a/java/ql/src/codeql-pack.release.yml +++ b/java/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.2.0 +lastReleaseVersion: 0.3.0 diff --git a/java/ql/src/qlpack.yml b/java/ql/src/qlpack.yml index 2366eef3778..b0790498fe3 100644 --- a/java/ql/src/qlpack.yml +++ b/java/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/java-queries -version: 0.2.1-dev +version: 0.3.0 groups: - java - queries diff --git a/javascript/ql/lib/CHANGELOG.md b/javascript/ql/lib/CHANGELOG.md index 9df72979ef3..23d54f955a7 100644 --- a/javascript/ql/lib/CHANGELOG.md +++ b/javascript/ql/lib/CHANGELOG.md @@ -1,3 +1,11 @@ +## 0.2.1 + +### Minor Analysis Improvements + +* The `chownr` library is now modeled as a sink for the `js/path-injection` query. +* Improved modeling of sensitive data sources, so common words like `certain` and `secretary` are no longer considered a certificate and a secret (respectively). +* The `gray-matter` library is now modeled as a sink for the `js/code-injection` query. + ## 0.2.0 ### Major Analysis Improvements diff --git a/javascript/ql/lib/change-notes/2022-06-22-sensitive-common-words copy.md b/javascript/ql/lib/change-notes/2022-06-22-sensitive-common-words copy.md deleted file mode 100644 index 8a0151df015..00000000000 --- a/javascript/ql/lib/change-notes/2022-06-22-sensitive-common-words copy.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* The `gray-matter` library is now modeled as a sink for the `js/code-injection` query. diff --git a/javascript/ql/lib/change-notes/2022-06-22-sensitive-common-words.md b/javascript/ql/lib/change-notes/2022-06-22-sensitive-common-words.md deleted file mode 100644 index 9102d58abdb..00000000000 --- a/javascript/ql/lib/change-notes/2022-06-22-sensitive-common-words.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* Improved modeling of sensitive data sources, so common words like `certain` and `secretary` are no longer considered a certificate and a secret (respectively). diff --git a/javascript/ql/lib/change-notes/2022-06-30-chownr.md b/javascript/ql/lib/change-notes/2022-06-30-chownr.md deleted file mode 100644 index 1ad13fb8113..00000000000 --- a/javascript/ql/lib/change-notes/2022-06-30-chownr.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* The `chownr` library is now modeled as a sink for the `js/path-injection` query. diff --git a/javascript/ql/lib/change-notes/released/0.2.1.md b/javascript/ql/lib/change-notes/released/0.2.1.md new file mode 100644 index 00000000000..3548bd5ad69 --- /dev/null +++ b/javascript/ql/lib/change-notes/released/0.2.1.md @@ -0,0 +1,7 @@ +## 0.2.1 + +### Minor Analysis Improvements + +* The `chownr` library is now modeled as a sink for the `js/path-injection` query. +* Improved modeling of sensitive data sources, so common words like `certain` and `secretary` are no longer considered a certificate and a secret (respectively). +* The `gray-matter` library is now modeled as a sink for the `js/code-injection` query. diff --git a/javascript/ql/lib/codeql-pack.release.yml b/javascript/ql/lib/codeql-pack.release.yml index 5274e27ed52..df29a726bcc 100644 --- a/javascript/ql/lib/codeql-pack.release.yml +++ b/javascript/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.2.0 +lastReleaseVersion: 0.2.1 diff --git a/javascript/ql/lib/qlpack.yml b/javascript/ql/lib/qlpack.yml index b16a8912ccf..cb4b01d90b0 100644 --- a/javascript/ql/lib/qlpack.yml +++ b/javascript/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/javascript-all -version: 0.2.1-dev +version: 0.2.1 groups: javascript dbscheme: semmlecode.javascript.dbscheme extractor: javascript diff --git a/javascript/ql/src/CHANGELOG.md b/javascript/ql/src/CHANGELOG.md index 68660fcbb52..baf7f9b85e0 100644 --- a/javascript/ql/src/CHANGELOG.md +++ b/javascript/ql/src/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.3.0 + +### Breaking Changes + +* Contextual queries and the query libraries they depend on have been moved to the `codeql/javascript-all` package. + ## 0.2.0 ### Minor Analysis Improvements diff --git a/javascript/ql/src/change-notes/2022-06-29-move-contextual-queries.md b/javascript/ql/src/change-notes/released/0.3.0.md similarity index 78% rename from javascript/ql/src/change-notes/2022-06-29-move-contextual-queries.md rename to javascript/ql/src/change-notes/released/0.3.0.md index ff190788cd4..13b4541cd4b 100644 --- a/javascript/ql/src/change-notes/2022-06-29-move-contextual-queries.md +++ b/javascript/ql/src/change-notes/released/0.3.0.md @@ -1,4 +1,5 @@ ---- -category: breaking ---- +## 0.3.0 + +### Breaking Changes + * Contextual queries and the query libraries they depend on have been moved to the `codeql/javascript-all` package. diff --git a/javascript/ql/src/codeql-pack.release.yml b/javascript/ql/src/codeql-pack.release.yml index 5274e27ed52..95f6e3a0ba6 100644 --- a/javascript/ql/src/codeql-pack.release.yml +++ b/javascript/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.2.0 +lastReleaseVersion: 0.3.0 diff --git a/javascript/ql/src/qlpack.yml b/javascript/ql/src/qlpack.yml index 4f54f7dc5bc..07ac096af64 100644 --- a/javascript/ql/src/qlpack.yml +++ b/javascript/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/javascript-queries -version: 0.2.1-dev +version: 0.3.0 groups: - javascript - queries diff --git a/python/ql/lib/CHANGELOG.md b/python/ql/lib/CHANGELOG.md index 83861bcf61d..83a09c70446 100644 --- a/python/ql/lib/CHANGELOG.md +++ b/python/ql/lib/CHANGELOG.md @@ -1,3 +1,18 @@ +## 0.5.1 + +### Deprecated APIs + +- The documentation of API graphs (the `API` module) has been expanded, and some of the members predicates of `API::Node` + have been renamed as follows: + - `getAnImmediateUse` -> `asSource` + - `getARhs` -> `asSink` + - `getAUse` -> `getAValueReachableFromSource` + - `getAValueReachingRhs` -> `getAValueReachingSink` + +### Minor Analysis Improvements + +* Improved modeling of sensitive data sources, so common words like `certain` and `secretary` are no longer considered a certificate and a secret (respectively). + ## 0.5.0 ### Deprecated APIs diff --git a/python/ql/lib/change-notes/2022-06-22-sensitive-common-words.md b/python/ql/lib/change-notes/2022-06-22-sensitive-common-words.md deleted file mode 100644 index 9102d58abdb..00000000000 --- a/python/ql/lib/change-notes/2022-06-22-sensitive-common-words.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* Improved modeling of sensitive data sources, so common words like `certain` and `secretary` are no longer considered a certificate and a secret (respectively). diff --git a/python/ql/lib/change-notes/2022-06-28-api-graph-api.md b/python/ql/lib/change-notes/released/0.5.1.md similarity index 58% rename from python/ql/lib/change-notes/2022-06-28-api-graph-api.md rename to python/ql/lib/change-notes/released/0.5.1.md index ef6ff3d4f76..1b560c7a35a 100644 --- a/python/ql/lib/change-notes/2022-06-28-api-graph-api.md +++ b/python/ql/lib/change-notes/released/0.5.1.md @@ -1,6 +1,6 @@ ---- -category: deprecated ---- +## 0.5.1 + +### Deprecated APIs - The documentation of API graphs (the `API` module) has been expanded, and some of the members predicates of `API::Node` have been renamed as follows: @@ -8,3 +8,7 @@ category: deprecated - `getARhs` -> `asSink` - `getAUse` -> `getAValueReachableFromSource` - `getAValueReachingRhs` -> `getAValueReachingSink` + +### Minor Analysis Improvements + +* Improved modeling of sensitive data sources, so common words like `certain` and `secretary` are no longer considered a certificate and a secret (respectively). diff --git a/python/ql/lib/codeql-pack.release.yml b/python/ql/lib/codeql-pack.release.yml index 30e271c5361..0bf7024c337 100644 --- a/python/ql/lib/codeql-pack.release.yml +++ b/python/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.5.0 +lastReleaseVersion: 0.5.1 diff --git a/python/ql/lib/qlpack.yml b/python/ql/lib/qlpack.yml index 65145e40d74..7aac7c78527 100644 --- a/python/ql/lib/qlpack.yml +++ b/python/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/python-all -version: 0.5.1-dev +version: 0.5.1 groups: python dbscheme: semmlecode.python.dbscheme extractor: python diff --git a/python/ql/src/CHANGELOG.md b/python/ql/src/CHANGELOG.md index 3be12c71c5f..fae4ab0dc9a 100644 --- a/python/ql/src/CHANGELOG.md +++ b/python/ql/src/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.3.0 + +### Breaking Changes + +* Contextual queries and the query libraries they depend on have been moved to the `codeql/python-all` package. + ## 0.2.0 ### Major Analysis Improvements diff --git a/python/ql/src/change-notes/2022-06-29-move-contextual-queries.md b/python/ql/src/change-notes/released/0.3.0.md similarity index 77% rename from python/ql/src/change-notes/2022-06-29-move-contextual-queries.md rename to python/ql/src/change-notes/released/0.3.0.md index 2e8562e66f8..7b54313449c 100644 --- a/python/ql/src/change-notes/2022-06-29-move-contextual-queries.md +++ b/python/ql/src/change-notes/released/0.3.0.md @@ -1,4 +1,5 @@ ---- -category: breaking ---- +## 0.3.0 + +### Breaking Changes + * Contextual queries and the query libraries they depend on have been moved to the `codeql/python-all` package. diff --git a/python/ql/src/codeql-pack.release.yml b/python/ql/src/codeql-pack.release.yml index 5274e27ed52..95f6e3a0ba6 100644 --- a/python/ql/src/codeql-pack.release.yml +++ b/python/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.2.0 +lastReleaseVersion: 0.3.0 diff --git a/python/ql/src/qlpack.yml b/python/ql/src/qlpack.yml index af722116e41..6d07cdc1ab3 100644 --- a/python/ql/src/qlpack.yml +++ b/python/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/python-queries -version: 0.2.1-dev +version: 0.3.0 groups: - python - queries diff --git a/ruby/ql/lib/CHANGELOG.md b/ruby/ql/lib/CHANGELOG.md index 1b060e46141..fe8a12aa938 100644 --- a/ruby/ql/lib/CHANGELOG.md +++ b/ruby/ql/lib/CHANGELOG.md @@ -1,3 +1,12 @@ +## 0.3.1 + +### Minor Analysis Improvements + +* Fixed a bug causing every expression in the database to be considered a system-command execution sink when calls to any of the following methods exist: + * The `spawn`, `fspawn`, `popen4`, `pspawn`, `system`, `_pspawn` methods and the backtick operator from the `POSIX::spawn` gem. + * The `execute_command`, `rake`, `rails_command`, and `git` methods in `Rails::Generation::Actions`. +* Improved modeling of sensitive data sources, so common words like `certain` and `secretary` are no longer considered a certificate and a secret (respectively). + ## 0.3.0 ### Deprecated APIs diff --git a/ruby/ql/lib/change-notes/2022-06-22-sensitive-common-words.md b/ruby/ql/lib/change-notes/2022-06-22-sensitive-common-words.md deleted file mode 100644 index 9102d58abdb..00000000000 --- a/ruby/ql/lib/change-notes/2022-06-22-sensitive-common-words.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* Improved modeling of sensitive data sources, so common words like `certain` and `secretary` are no longer considered a certificate and a secret (respectively). diff --git a/ruby/ql/lib/change-notes/2022-07-11-command-execution.md b/ruby/ql/lib/change-notes/released/0.3.1.md similarity index 65% rename from ruby/ql/lib/change-notes/2022-07-11-command-execution.md rename to ruby/ql/lib/change-notes/released/0.3.1.md index f6548719b57..59f378bbb32 100644 --- a/ruby/ql/lib/change-notes/2022-07-11-command-execution.md +++ b/ruby/ql/lib/change-notes/released/0.3.1.md @@ -1,6 +1,8 @@ ---- -category: minorAnalysis ---- +## 0.3.1 + +### Minor Analysis Improvements + * Fixed a bug causing every expression in the database to be considered a system-command execution sink when calls to any of the following methods exist: * The `spawn`, `fspawn`, `popen4`, `pspawn`, `system`, `_pspawn` methods and the backtick operator from the `POSIX::spawn` gem. * The `execute_command`, `rake`, `rails_command`, and `git` methods in `Rails::Generation::Actions`. +* Improved modeling of sensitive data sources, so common words like `certain` and `secretary` are no longer considered a certificate and a secret (respectively). diff --git a/ruby/ql/lib/codeql-pack.release.yml b/ruby/ql/lib/codeql-pack.release.yml index 95f6e3a0ba6..bb106b1cb63 100644 --- a/ruby/ql/lib/codeql-pack.release.yml +++ b/ruby/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.3.0 +lastReleaseVersion: 0.3.1 diff --git a/ruby/ql/lib/qlpack.yml b/ruby/ql/lib/qlpack.yml index 062d906c9a2..5b67e5ccd50 100644 --- a/ruby/ql/lib/qlpack.yml +++ b/ruby/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/ruby-all -version: 0.3.1-dev +version: 0.3.1 groups: ruby extractor: ruby dbscheme: ruby.dbscheme diff --git a/ruby/ql/src/CHANGELOG.md b/ruby/ql/src/CHANGELOG.md index 0905f133d16..9f227fdc843 100644 --- a/ruby/ql/src/CHANGELOG.md +++ b/ruby/ql/src/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.3.0 + +### Breaking Changes + +* Contextual queries and the query libraries they depend on have been moved to the `codeql/ruby-all` package. + ## 0.2.0 ### New Queries diff --git a/ruby/ql/src/change-notes/2022-06-29-move-contextual-queries.md b/ruby/ql/src/change-notes/released/0.3.0.md similarity index 77% rename from ruby/ql/src/change-notes/2022-06-29-move-contextual-queries.md rename to ruby/ql/src/change-notes/released/0.3.0.md index bc85eb9361e..717a3a27aa5 100644 --- a/ruby/ql/src/change-notes/2022-06-29-move-contextual-queries.md +++ b/ruby/ql/src/change-notes/released/0.3.0.md @@ -1,4 +1,5 @@ ---- -category: breaking ---- +## 0.3.0 + +### Breaking Changes + * Contextual queries and the query libraries they depend on have been moved to the `codeql/ruby-all` package. diff --git a/ruby/ql/src/codeql-pack.release.yml b/ruby/ql/src/codeql-pack.release.yml index 5274e27ed52..95f6e3a0ba6 100644 --- a/ruby/ql/src/codeql-pack.release.yml +++ b/ruby/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.2.0 +lastReleaseVersion: 0.3.0 diff --git a/ruby/ql/src/qlpack.yml b/ruby/ql/src/qlpack.yml index 0bd19b5bdeb..6a55e6cecd3 100644 --- a/ruby/ql/src/qlpack.yml +++ b/ruby/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/ruby-queries -version: 0.2.1-dev +version: 0.3.0 groups: - ruby - queries From fe1f1bb79d0143fc3fba77a42df8e0eb219eafe2 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema <93738568+jketema@users.noreply.github.com> Date: Thu, 14 Jul 2022 11:06:14 +0200 Subject: [PATCH 246/505] Fix issues with change notes --- cpp/ql/lib/CHANGELOG.md | 2 +- cpp/ql/lib/change-notes/released/0.3.1.md | 2 +- java/ql/lib/CHANGELOG.md | 4 ++-- java/ql/lib/change-notes/released/0.3.1.md | 4 ++-- java/ql/src/CHANGELOG.md | 4 ++-- java/ql/src/change-notes/released/0.3.0.md | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cpp/ql/lib/CHANGELOG.md b/cpp/ql/lib/CHANGELOG.md index 3e3f0b4531b..d954c60fb0a 100644 --- a/cpp/ql/lib/CHANGELOG.md +++ b/cpp/ql/lib/CHANGELOG.md @@ -2,7 +2,7 @@ ### Minor Analysis Improvements -* `AnalysedExpr::isNullCheck` and `AnalysedExpr::isValidCheck` have been updated to handle variable accesses on the left-hand side of the the C++ logical and variable declarations in conditions. +* `AnalysedExpr::isNullCheck` and `AnalysedExpr::isValidCheck` have been updated to handle variable accesses on the left-hand side of the the C++ logical "and", and variable declarations in conditions. ## 0.3.0 diff --git a/cpp/ql/lib/change-notes/released/0.3.1.md b/cpp/ql/lib/change-notes/released/0.3.1.md index 4b9a3206c96..235ad58d370 100644 --- a/cpp/ql/lib/change-notes/released/0.3.1.md +++ b/cpp/ql/lib/change-notes/released/0.3.1.md @@ -2,4 +2,4 @@ ### Minor Analysis Improvements -* `AnalysedExpr::isNullCheck` and `AnalysedExpr::isValidCheck` have been updated to handle variable accesses on the left-hand side of the the C++ logical and variable declarations in conditions. +* `AnalysedExpr::isNullCheck` and `AnalysedExpr::isValidCheck` have been updated to handle variable accesses on the left-hand side of the the C++ logical "and", and variable declarations in conditions. diff --git a/java/ql/lib/CHANGELOG.md b/java/ql/lib/CHANGELOG.md index 6b5e4ec9f09..b5ceb823e75 100644 --- a/java/ql/lib/CHANGELOG.md +++ b/java/ql/lib/CHANGELOG.md @@ -7,9 +7,9 @@ ### Minor Analysis Improvements * Added data-flow models for `java.util.Properites`. Additional results may be found where relevant data is stored in and then retrieved from a `Properties` instance. -Added `Modifier.isInline()`. +* Added `Modifier.isInline()`. * Removed Kotlin-specific database and QL structures for loops and `break`/`continue` statements. The Kotlin extractor was changed to reuse the Java structures for these constructs. -Added additional flow sources for uses of external storage on Android. +* Added additional flow sources for uses of external storage on Android. ## 0.3.0 diff --git a/java/ql/lib/change-notes/released/0.3.1.md b/java/ql/lib/change-notes/released/0.3.1.md index 8c0e3158b0b..a453f034d5c 100644 --- a/java/ql/lib/change-notes/released/0.3.1.md +++ b/java/ql/lib/change-notes/released/0.3.1.md @@ -7,6 +7,6 @@ ### Minor Analysis Improvements * Added data-flow models for `java.util.Properites`. Additional results may be found where relevant data is stored in and then retrieved from a `Properties` instance. -Added `Modifier.isInline()`. +* Added `Modifier.isInline()`. * Removed Kotlin-specific database and QL structures for loops and `break`/`continue` statements. The Kotlin extractor was changed to reuse the Java structures for these constructs. -Added additional flow sources for uses of external storage on Android. +* Added additional flow sources for uses of external storage on Android. diff --git a/java/ql/src/CHANGELOG.md b/java/ql/src/CHANGELOG.md index fc9988443b2..b39e648bf04 100644 --- a/java/ql/src/CHANGELOG.md +++ b/java/ql/src/CHANGELOG.md @@ -7,8 +7,8 @@ ### New Queries * A new query "Improper verification of intent by broadcast receiver" (`java/improper-intent-verification`) has been added. -This query finds instances of Android `BroadcastReceiver`s that don't verify the action string of received intents when registered -to receive system intents. + This query finds instances of Android `BroadcastReceiver`s that don't verify the action string of received intents when registered + to receive system intents. ## 0.2.0 diff --git a/java/ql/src/change-notes/released/0.3.0.md b/java/ql/src/change-notes/released/0.3.0.md index 48b478ec905..d91c653a471 100644 --- a/java/ql/src/change-notes/released/0.3.0.md +++ b/java/ql/src/change-notes/released/0.3.0.md @@ -7,5 +7,5 @@ ### New Queries * A new query "Improper verification of intent by broadcast receiver" (`java/improper-intent-verification`) has been added. -This query finds instances of Android `BroadcastReceiver`s that don't verify the action string of received intents when registered -to receive system intents. + This query finds instances of Android `BroadcastReceiver`s that don't verify the action string of received intents when registered + to receive system intents. From 9f184ec122ffed2d532e5d037db2f76b126138ad Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 14 Jul 2022 12:09:58 +0200 Subject: [PATCH 247/505] Update cpp/ql/lib/change-notes/released/0.3.1.md --- cpp/ql/lib/change-notes/released/0.3.1.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/lib/change-notes/released/0.3.1.md b/cpp/ql/lib/change-notes/released/0.3.1.md index 235ad58d370..d5b251c42ab 100644 --- a/cpp/ql/lib/change-notes/released/0.3.1.md +++ b/cpp/ql/lib/change-notes/released/0.3.1.md @@ -2,4 +2,4 @@ ### Minor Analysis Improvements -* `AnalysedExpr::isNullCheck` and `AnalysedExpr::isValidCheck` have been updated to handle variable accesses on the left-hand side of the the C++ logical "and", and variable declarations in conditions. +* `AnalysedExpr::isNullCheck` and `AnalysedExpr::isValidCheck` have been updated to handle variable accesses on the left-hand side of the C++ logical "and", and variable declarations in conditions. From dbff20a3d8854f2707a24580887ce22d544f4a86 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 14 Jul 2022 12:10:03 +0200 Subject: [PATCH 248/505] Update cpp/ql/lib/CHANGELOG.md --- cpp/ql/lib/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/lib/CHANGELOG.md b/cpp/ql/lib/CHANGELOG.md index d954c60fb0a..75a047d6f64 100644 --- a/cpp/ql/lib/CHANGELOG.md +++ b/cpp/ql/lib/CHANGELOG.md @@ -2,7 +2,7 @@ ### Minor Analysis Improvements -* `AnalysedExpr::isNullCheck` and `AnalysedExpr::isValidCheck` have been updated to handle variable accesses on the left-hand side of the the C++ logical "and", and variable declarations in conditions. +* `AnalysedExpr::isNullCheck` and `AnalysedExpr::isValidCheck` have been updated to handle variable accesses on the left-hand side of the C++ logical "and", and variable declarations in conditions. ## 0.3.0 From f20c18627713ba1d3578ec2aafbbbcb39d451710 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 14 Jul 2022 12:20:30 +0200 Subject: [PATCH 249/505] add ql/repeated-word query --- ql/ql/src/codeql_ql/ast/Ast.qll | 10 +++++++ ql/ql/src/codeql_ql/ast/internal/AstNodes.qll | 3 ++ ql/ql/src/queries/style/RepeatedWord.ql | 30 +++++++++++++++++++ 3 files changed, 43 insertions(+) create mode 100644 ql/ql/src/queries/style/RepeatedWord.ql diff --git a/ql/ql/src/codeql_ql/ast/Ast.qll b/ql/ql/src/codeql_ql/ast/Ast.qll index e399128c09a..4b3c00df10d 100644 --- a/ql/ql/src/codeql_ql/ast/Ast.qll +++ b/ql/ql/src/codeql_ql/ast/Ast.qll @@ -178,6 +178,16 @@ class BlockComment extends TBlockComment, AstNode { override string getAPrimaryQlClass() { result = "BlockComment" } } +class LineComment extends TLineComment, AstNode { + QL::LineComment comment; + + LineComment() { this = TLineComment(comment) } + + string getContents() { result = comment.getValue() } + + override string getAPrimaryQlClass() { result = "LineComment" } +} + /** * The `from, where, select` part of a QL query. */ diff --git a/ql/ql/src/codeql_ql/ast/internal/AstNodes.qll b/ql/ql/src/codeql_ql/ast/internal/AstNodes.qll index 9448fe71240..312f7f90c60 100644 --- a/ql/ql/src/codeql_ql/ast/internal/AstNodes.qll +++ b/ql/ql/src/codeql_ql/ast/internal/AstNodes.qll @@ -7,6 +7,7 @@ newtype TAstNode = TTopLevel(QL::Ql file) or TQLDoc(QL::Qldoc qldoc) or TBlockComment(QL::BlockComment comment) or + TLineComment(QL::LineComment comment) or TClasslessPredicate(QL::ClasslessPredicate pred) or TVarDecl(QL::VarDecl decl) or TFieldDecl(QL::Field field) or @@ -154,6 +155,8 @@ QL::AstNode toQL(AST::AstNode n) { or n = TBlockComment(result) or + n = TLineComment(result) + or n = TClasslessPredicate(result) or n = TVarDecl(result) diff --git a/ql/ql/src/queries/style/RepeatedWord.ql b/ql/ql/src/queries/style/RepeatedWord.ql new file mode 100644 index 00000000000..90ce45344dd --- /dev/null +++ b/ql/ql/src/queries/style/RepeatedWord.ql @@ -0,0 +1,30 @@ +/** + * @name Comment has repeated word + * @description Comment has repeated word. + * @kind problem + * @problem.severity warning + * @id ql/repeated-word + * @precision very-high + */ + +import ql + +string getComment(AstNode node) { + result = node.(QLDoc).getContents() + or + result = node.(BlockComment).getContents() + or + result = node.(LineComment).getContents() +} + +/** Gets a word used in `node` */ +string getWord(AstNode node) { result = getComment(node).regexpFind("\\b[A-Za-z]+\\b", _, _) } + +AstNode hasRepeatedWord(string word) { + word = getWord(result) and + getComment(result).regexpMatch(".*\\b" + word + "\\s+" + word + "\\b.*") +} + +from AstNode comment, string word +where comment = hasRepeatedWord(word) +select comment, "The comment repeats " + word + "." From cb3a0fb5deecb99e4bc7809d0a19b61fe6ee74b6 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 14 Jul 2022 12:25:01 +0200 Subject: [PATCH 250/505] make a Comment superclass --- ql/ql/src/codeql_ql/ast/Ast.qll | 16 ++++++++++------ ql/ql/src/codeql_ql/ast/internal/AstNodes.qll | 2 ++ ql/ql/src/queries/style/RepeatedWord.ql | 16 ++++------------ 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/ql/ql/src/codeql_ql/ast/Ast.qll b/ql/ql/src/codeql_ql/ast/Ast.qll index 4b3c00df10d..03a26868657 100644 --- a/ql/ql/src/codeql_ql/ast/Ast.qll +++ b/ql/ql/src/codeql_ql/ast/Ast.qll @@ -156,34 +156,38 @@ class TopLevel extends TTopLevel, AstNode { override QLDoc getQLDoc() { result = this.getMember(0) } } -class QLDoc extends TQLDoc, AstNode { +abstract class Comment extends AstNode, TComment { + abstract string getContents(); +} + +class QLDoc extends TQLDoc, Comment { QL::Qldoc qldoc; QLDoc() { this = TQLDoc(qldoc) } - string getContents() { result = qldoc.getValue() } + override string getContents() { result = qldoc.getValue() } override string getAPrimaryQlClass() { result = "QLDoc" } override AstNode getParent() { result.getQLDoc() = this } } -class BlockComment extends TBlockComment, AstNode { +class BlockComment extends TBlockComment, Comment { QL::BlockComment comment; BlockComment() { this = TBlockComment(comment) } - string getContents() { result = comment.getValue() } + override string getContents() { result = comment.getValue() } override string getAPrimaryQlClass() { result = "BlockComment" } } -class LineComment extends TLineComment, AstNode { +class LineComment extends TLineComment, Comment { QL::LineComment comment; LineComment() { this = TLineComment(comment) } - string getContents() { result = comment.getValue() } + override string getContents() { result = comment.getValue() } override string getAPrimaryQlClass() { result = "LineComment" } } diff --git a/ql/ql/src/codeql_ql/ast/internal/AstNodes.qll b/ql/ql/src/codeql_ql/ast/internal/AstNodes.qll index 312f7f90c60..ce5cd352dd8 100644 --- a/ql/ql/src/codeql_ql/ast/internal/AstNodes.qll +++ b/ql/ql/src/codeql_ql/ast/internal/AstNodes.qll @@ -90,6 +90,8 @@ class TYamlNode = TYamlCommemt or TYamlEntry or TYamlKey or TYamlListitem or TYa class TSignatureExpr = TPredicateExpr or TType; +class TComment = TQLDoc or TBlockComment or TLineComment; + /** DEPRECATED: Alias for TYamlNode */ deprecated class TYAMLNode = TYamlNode; diff --git a/ql/ql/src/queries/style/RepeatedWord.ql b/ql/ql/src/queries/style/RepeatedWord.ql index 90ce45344dd..f8e0ddd1018 100644 --- a/ql/ql/src/queries/style/RepeatedWord.ql +++ b/ql/ql/src/queries/style/RepeatedWord.ql @@ -9,22 +9,14 @@ import ql -string getComment(AstNode node) { - result = node.(QLDoc).getContents() - or - result = node.(BlockComment).getContents() - or - result = node.(LineComment).getContents() -} - /** Gets a word used in `node` */ -string getWord(AstNode node) { result = getComment(node).regexpFind("\\b[A-Za-z]+\\b", _, _) } +string getWord(Comment node) { result = node.getContents().regexpFind("\\b[A-Za-z]+\\b", _, _) } -AstNode hasRepeatedWord(string word) { +Comment hasRepeatedWord(string word) { word = getWord(result) and - getComment(result).regexpMatch(".*\\b" + word + "\\s+" + word + "\\b.*") + result.getContents().regexpMatch(".*\\b" + word + "\\s+" + word + "\\b.*") } -from AstNode comment, string word +from Comment comment, string word where comment = hasRepeatedWord(word) select comment, "The comment repeats " + word + "." From 2ea2bd89663f5a3c82778fba475ea2fa57f757a1 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 14 Jul 2022 12:35:09 +0200 Subject: [PATCH 251/505] refine the repeated-word query --- ql/ql/src/queries/style/RepeatedWord.ql | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ql/ql/src/queries/style/RepeatedWord.ql b/ql/ql/src/queries/style/RepeatedWord.ql index f8e0ddd1018..cbbed4668bd 100644 --- a/ql/ql/src/queries/style/RepeatedWord.ql +++ b/ql/ql/src/queries/style/RepeatedWord.ql @@ -14,9 +14,12 @@ string getWord(Comment node) { result = node.getContents().regexpFind("\\b[A-Za- Comment hasRepeatedWord(string word) { word = getWord(result) and - result.getContents().regexpMatch(".*\\b" + word + "\\s+" + word + "\\b.*") + result.getContents().regexpMatch(".*[\\s]" + word + "\\s+" + word + "[\\s.,].*") } from Comment comment, string word -where comment = hasRepeatedWord(word) +where + comment = hasRepeatedWord(word) and + // lots of these, and I can't just change old dbschemes. + not (word = "type" and comment.getLocation().getFile().getExtension() = "dbscheme") select comment, "The comment repeats " + word + "." From 85a652f3d104ad10a6fbaaa43731e48363b8e5d6 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 14 Jul 2022 12:35:23 +0200 Subject: [PATCH 252/505] remove a bunch of repeated words --- cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll | 2 +- .../lib/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll | 2 +- .../code/csharp/controlflow/internal/ControlFlowGraphImpl.qll | 2 +- csharp/ql/src/Telemetry/ExternalApi.qll | 2 +- go/ql/src/RedundantCode/DuplicateSwitchCase.ql | 2 +- java/ql/src/Telemetry/ExternalApi.qll | 2 +- python/ql/lib/semmle/python/Frameworks.qll | 2 +- python/ql/lib/semmle/python/dataflow/old/TaintTracking.qll | 2 +- ql/ql/src/codeql_ql/ast/internal/Module.qll | 2 +- ql/ql/src/queries/performance/MissingNomagic.ql | 2 +- ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll | 2 +- ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll | 2 +- ruby/ql/lib/codeql/ruby/frameworks/GraphQL.qll | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll index da8e7564b3e..74ba3569d20 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll @@ -699,7 +699,7 @@ private predicate exprToExprStep_nocfg(Expr fromExpr, Expr toExpr) { call.getTarget() = f and // AST dataflow treats a reference as if it were the referred-to object, while the dataflow // models treat references as pointers. If the return type of the call is a reference, then - // look for data flow the the referred-to object, rather than the reference itself. + // look for data flow the referred-to object, rather than the reference itself. if call.getType().getUnspecifiedType() instanceof ReferenceType then outModel.isReturnValueDeref() else outModel.isReturnValue() diff --git a/cpp/ql/lib/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll b/cpp/ql/lib/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll index c9e790d9077..0f47770dc82 100644 --- a/cpp/ql/lib/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll +++ b/cpp/ql/lib/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll @@ -1573,7 +1573,7 @@ private module SimpleRangeAnalysisCached { result = min([max(getTruncatedUpperBounds(expr)), getGuardedUpperBound(expr)]) } - /** Holds if the upper bound of `expr` may have been widened. This means the the upper bound is in practice likely to be overly wide. */ + /** Holds if the upper bound of `expr` may have been widened. This means the upper bound is in practice likely to be overly wide. */ cached predicate upperBoundMayBeWidened(Expr e) { isRecursiveExpr(e) and diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll index d9bebb9bf17..b63d804216b 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll @@ -335,7 +335,7 @@ module Expressions { // ```csharp // new Dictionary() { [0] = "Zero", [1] = "One", [2] = "Two" } // ``` - // need special treatment, because the the accesses `[0]`, `[1]`, and `[2]` + // need special treatment, because the accesses `[0]`, `[1]`, and `[2]` // have no qualifier. this = any(MemberInitializer mi).getLValue() } diff --git a/csharp/ql/src/Telemetry/ExternalApi.qll b/csharp/ql/src/Telemetry/ExternalApi.qll index 9679cd1061a..685eda64e50 100644 --- a/csharp/ql/src/Telemetry/ExternalApi.qll +++ b/csharp/ql/src/Telemetry/ExternalApi.qll @@ -85,7 +85,7 @@ class ExternalApi extends DotNet::Callable { defaultAdditionalTaintStep(this.getAnInput(), _) } - /** Holds if this API is is a constructor without parameters. */ + /** Holds if this API is a constructor without parameters. */ private predicate isParameterlessConstructor() { this instanceof Constructor and this.getNumberOfParameters() = 0 } diff --git a/go/ql/src/RedundantCode/DuplicateSwitchCase.ql b/go/ql/src/RedundantCode/DuplicateSwitchCase.ql index 46956347785..7af97b76875 100644 --- a/go/ql/src/RedundantCode/DuplicateSwitchCase.ql +++ b/go/ql/src/RedundantCode/DuplicateSwitchCase.ql @@ -13,7 +13,7 @@ import go -/** Gets the global value number of of `e`, which is the `i`th case label of `switch`. */ +/** Gets the global value number of `e`, which is the `i`th case label of `switch`. */ GVN switchCaseGVN(SwitchStmt switch, int i, Expr e) { e = switch.getCase(i).getExpr(0) and result = e.getGlobalValueNumber() } diff --git a/java/ql/src/Telemetry/ExternalApi.qll b/java/ql/src/Telemetry/ExternalApi.qll index b839ca15c6d..eaf88555444 100644 --- a/java/ql/src/Telemetry/ExternalApi.qll +++ b/java/ql/src/Telemetry/ExternalApi.qll @@ -73,7 +73,7 @@ class ExternalApi extends Callable { TaintTracking::localAdditionalTaintStep(this.getAnInput(), _) } - /** Holds if this API is is a constructor without parameters. */ + /** Holds if this API is a constructor without parameters. */ private predicate isParameterlessConstructor() { this instanceof Constructor and this.getNumberOfParameters() = 0 } diff --git a/python/ql/lib/semmle/python/Frameworks.qll b/python/ql/lib/semmle/python/Frameworks.qll index daa67ee4231..6cf4044157b 100644 --- a/python/ql/lib/semmle/python/Frameworks.qll +++ b/python/ql/lib/semmle/python/Frameworks.qll @@ -2,7 +2,7 @@ * Helper file that imports all framework modeling. */ -// If you add modeling of a new framework/library, remember to add it it to the docs in +// If you add modeling of a new framework/library, remember to add it to the docs in // `docs/codeql/support/reusables/frameworks.rst` private import semmle.python.frameworks.Aioch private import semmle.python.frameworks.Aiohttp diff --git a/python/ql/lib/semmle/python/dataflow/old/TaintTracking.qll b/python/ql/lib/semmle/python/dataflow/old/TaintTracking.qll index 22832b46c6c..d0293522404 100755 --- a/python/ql/lib/semmle/python/dataflow/old/TaintTracking.qll +++ b/python/ql/lib/semmle/python/dataflow/old/TaintTracking.qll @@ -333,7 +333,7 @@ abstract class Sanitizer extends string { /** Holds if `taint` cannot flow through `node`. */ predicate sanitizingNode(TaintKind taint, ControlFlowNode node) { none() } - /** Holds if `call` removes removes the `taint` */ + /** Holds if `call` removes the `taint` */ predicate sanitizingCall(TaintKind taint, FunctionObject callee) { none() } /** Holds if `test` shows value to be untainted with `taint` */ diff --git a/ql/ql/src/codeql_ql/ast/internal/Module.qll b/ql/ql/src/codeql_ql/ast/internal/Module.qll index 91a5888ac12..8c1e6189e0a 100644 --- a/ql/ql/src/codeql_ql/ast/internal/Module.qll +++ b/ql/ql/src/codeql_ql/ast/internal/Module.qll @@ -78,7 +78,7 @@ private class Folder_ extends ContainerOrModule, TFolder { override ContainerOrModule getEnclosing() { result = TFolder(f.getParentContainer()) and - // if this the the root, then we stop. + // if this the root, then we stop. not exists(f.getFile("qlpack.yml")) } diff --git a/ql/ql/src/queries/performance/MissingNomagic.ql b/ql/ql/src/queries/performance/MissingNomagic.ql index 13084bf6ad6..ebab0e7b7e5 100644 --- a/ql/ql/src/queries/performance/MissingNomagic.ql +++ b/ql/ql/src/queries/performance/MissingNomagic.ql @@ -57,7 +57,7 @@ class CandidatePredicate extends Predicate { this.getName() .regexpCapture("(.+)" + ["0", "helper", "aux", "cand", "Helper", "Aux", "Cand"], 1) or - // Or this this predicate is named "foo02" and `pred` is named "foo01". + // Or this predicate is named "foo02" and `pred` is named "foo01". exists(int n, string name | hasNameWithNumberSuffix(pred, name, n) and hasNameWithNumberSuffix(this, name, n - 1) diff --git a/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll b/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll index efb69af39eb..95a0514320e 100644 --- a/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll +++ b/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll @@ -348,7 +348,7 @@ module ExprNodes { /** Gets an argument of this call. */ final ExprCfgNode getAnArgument() { result = this.getArgument(_) } - /** Gets the the keyword argument whose key is `keyword` of this call. */ + /** Gets the keyword argument whose key is `keyword` of this call. */ final ExprCfgNode getKeywordArgument(string keyword) { exists(PairCfgNode n | e.hasCfgChild(e.getAnArgument(), this, n) and diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll index 730445d99ac..11aa33b7551 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll @@ -71,7 +71,7 @@ class CallNode extends LocalSourceNode, ExprNode { /** Gets the data-flow node corresponding to the named argument of the call corresponding to this data-flow node */ ExprNode getKeywordArgument(string name) { result.getExprNode() = node.getKeywordArgument(name) } - /** Gets the name of the the method called by the method call (if any) corresponding to this data-flow node */ + /** Gets the name of the method called by the method call (if any) corresponding to this data-flow node */ string getMethodName() { result = node.getExpr().(MethodCall).getMethodName() } /** Gets the number of arguments of this call. */ diff --git a/ruby/ql/lib/codeql/ruby/frameworks/GraphQL.qll b/ruby/ql/lib/codeql/ruby/frameworks/GraphQL.qll index 3dacd89b25e..ede99069213 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/GraphQL.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/GraphQL.qll @@ -379,7 +379,7 @@ class GraphqlFieldResolutionMethod extends Method, HTTP::Server::RequestHandler: result.(KeywordParameter).hasName(argDefn.getArgumentName()) or // TODO this will cause false positives because now *anything* in the **args - // param will be flagged as as RoutedParameter/RemoteFlowSource, but really + // param will be flagged as RoutedParameter/RemoteFlowSource, but really // only the hash keys corresponding to the defined arguments are user input // others could be things defined in the `:extras` keyword argument to the `argument` result instanceof HashSplatParameter // often you see `def field(**args)` From 380070f2e474912a700a41d813db04e901520517 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 14 Jul 2022 12:50:23 +0200 Subject: [PATCH 253/505] rewrite the QL-for-QL workflow to just do everything in one go --- .github/workflows/ql-for-ql-build.yml | 70 +++++---------------------- 1 file changed, 11 insertions(+), 59 deletions(-) diff --git a/.github/workflows/ql-for-ql-build.yml b/.github/workflows/ql-for-ql-build.yml index 2d96fbfa797..7d8efa9e7ca 100644 --- a/.github/workflows/ql-for-ql-build.yml +++ b/.github/workflows/ql-for-ql-build.yml @@ -10,9 +10,10 @@ env: CARGO_TERM_COLOR: always jobs: - queries: - runs-on: ubuntu-latest + all-the-things: + runs-on: ubuntu-latest-xl steps: + ### Build the queries ### - uses: actions/checkout@v3 - name: Find codeql id: find-codeql @@ -48,11 +49,7 @@ jobs: name: query-pack-zip path: ${{ runner.temp }}/query-pack.zip - extractors: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 + ### Build the extractor ### - name: Cache entire extractor id: cache-extractor uses: actions/cache@v3 @@ -96,15 +93,8 @@ jobs: ql/target/release/ql-extractor ql/target/release/ql-extractor.exe retention-days: 1 - package: - runs-on: ubuntu-latest - needs: - - extractors - - queries - - steps: - - uses: actions/checkout@v3 + ### Package the queries and extractor ### - uses: actions/download-artifact@v3 with: name: query-pack-zip @@ -132,16 +122,8 @@ jobs: name: codeql-ql-pack path: codeql-ql.zip retention-days: 1 - analyze: - runs-on: ubuntu-latest - strategy: - matrix: - folder: [cpp, csharp, java, javascript, python, ql, ruby, swift, go] - needs: - - package - - steps: + ### Run the analysis ### - name: Download pack uses: actions/download-artifact@v3 with: @@ -161,12 +143,8 @@ jobs: env: PACK: ${{ runner.temp }}/pack - - name: Checkout repository - uses: actions/checkout@v3 - name: Create CodeQL config file run: | - echo "paths:" > ${CONF} - echo " - ${FOLDER}" >> ${CONF} echo "paths-ignore:" >> ${CONF} echo " - ql/ql/test" >> ${CONF} echo "disable-default-queries: true" >> ${CONF} @@ -176,7 +154,6 @@ jobs: cat ${CONF} env: CONF: ./ql-for-ql-config.yml - FOLDER: ${{ matrix.folder }} - name: Initialize CodeQL uses: github/codeql-action/init@aa93aea877e5fb8841bcb1193f672abf6e9f2980 with: @@ -187,39 +164,14 @@ jobs: - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@aa93aea877e5fb8841bcb1193f672abf6e9f2980 with: - category: "ql-for-ql-${{ matrix.folder }}" + category: "ql-for-ql" - name: Copy sarif file to CWD - run: cp ../results/ql.sarif ./${{ matrix.folder }}.sarif + run: cp ../results/ql.sarif ./ql-for-ql.sarif - name: Fixup the $scema in sarif # Until https://github.com/microsoft/sarif-vscode-extension/pull/436/ is part in a stable release run: | - sed -i 's/\$schema.*/\$schema": "https:\/\/raw.githubusercontent.com\/oasis-tcs\/sarif-spec\/master\/Schemata\/sarif-schema-2.1.0",/' ${{ matrix.folder }}.sarif + sed -i 's/\$schema.*/\$schema": "https:\/\/raw.githubusercontent.com\/oasis-tcs\/sarif-spec\/master\/Schemata\/sarif-schema-2.1.0",/' ql-for-ql.sarif - name: Sarif as artifact uses: actions/upload-artifact@v3 with: - name: ${{ matrix.folder }}.sarif - path: ${{ matrix.folder }}.sarif - - combine: - runs-on: ubuntu-latest - needs: - - analyze - - steps: - - uses: actions/checkout@v3 - - name: Make a folder for artifacts. - run: mkdir -p results - - name: Download all sarif files - uses: actions/download-artifact@v3 - with: - path: results - - uses: actions/setup-node@v3 - with: - node-version: 16 - - name: Combine all sarif files - run: | - node ./ql/scripts/merge-sarif.js results/**/*.sarif combined.sarif - - name: Upload combined sarif file - uses: actions/upload-artifact@v3 - with: - name: combined.sarif - path: combined.sarif \ No newline at end of file + name: ql-for-ql.sarif + path: ql-for-ql.sarif From 47c9b446f0f068069f5caee83a2e1f16d3908642 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 14 Jul 2022 12:54:53 +0200 Subject: [PATCH 254/505] exclude upgrade scripts from QL-for-QL --- .github/workflows/ql-for-ql-build.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ql-for-ql-build.yml b/.github/workflows/ql-for-ql-build.yml index 7d8efa9e7ca..c971d28bb2f 100644 --- a/.github/workflows/ql-for-ql-build.yml +++ b/.github/workflows/ql-for-ql-build.yml @@ -10,7 +10,7 @@ env: CARGO_TERM_COLOR: always jobs: - all-the-things: + analyze: runs-on: ubuntu-latest-xl steps: ### Build the queries ### @@ -147,6 +147,7 @@ jobs: run: | echo "paths-ignore:" >> ${CONF} echo " - ql/ql/test" >> ${CONF} + echo " - \"*/ql/lib/upgrades/\"" >> ${CONF} echo "disable-default-queries: true" >> ${CONF} echo "packs:" >> ${CONF} echo " - codeql/ql" >> ${CONF} From a7a9428dc1aed8f21563bc8fa37e8b1d148b71b4 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 14 Jul 2022 13:20:52 +0200 Subject: [PATCH 255/505] split the sarif file into languages --- .github/workflows/ql-for-ql-build.yml | 10 +++++++++ ql/scripts/split-sarif.js | 30 +++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 ql/scripts/split-sarif.js diff --git a/.github/workflows/ql-for-ql-build.yml b/.github/workflows/ql-for-ql-build.yml index c971d28bb2f..f5df6291b62 100644 --- a/.github/workflows/ql-for-ql-build.yml +++ b/.github/workflows/ql-for-ql-build.yml @@ -176,3 +176,13 @@ jobs: with: name: ql-for-ql.sarif path: ql-for-ql.sarif + - name: Split out the sarif file into langs + run: | + mkdir split-sarif + node ./ql/scripts/split-sarif.js ql-for-ql.sarif split-sarif + - name: Upload langs as artifacts + uses: actions/upload-artifact@v3 + with: + name: ql-for-ql-langs + path: split-sarif + retention-days: 1 \ No newline at end of file diff --git a/ql/scripts/split-sarif.js b/ql/scripts/split-sarif.js new file mode 100644 index 00000000000..d09989abf8a --- /dev/null +++ b/ql/scripts/split-sarif.js @@ -0,0 +1,30 @@ +var fs = require("fs"); + +// the .sarif file to split, and then the directory to put the split files in. +async function main(inputs) { + const sarifFile = JSON.parse(fs.readFileSync(inputs[0])); + const outFolder = inputs[1]; + + const out = {}; + + for (const result of sarifFile.runs[0].results) { + const lang = getLanguage(result); + if (!out[lang]) { + out[lang] = []; + } + out[lang].push(result); + } + + for (const lang in out) { + const outSarif = JSON.parse(JSON.stringify(sarifFile)); + outSarif.runs[0].results = out[lang]; + fs.writeFileSync(`${outFolder}/${lang}.sarif`, JSON.stringify(outSarif, null, 2)); + } +} + +function getLanguage(result) { + return result.locations[0].physicalLocation.artifactLocation.uri.split( + "/" + )[0]; +} +main(process.argv.splice(2)); From 1037c2b18213ed1efaf4113c2314417c8401b3ac Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 14 Jul 2022 13:30:12 +0200 Subject: [PATCH 256/505] all comments are alive --- ql/ql/src/codeql_ql/style/DeadCodeQuery.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ql/ql/src/codeql_ql/style/DeadCodeQuery.qll b/ql/ql/src/codeql_ql/style/DeadCodeQuery.qll index 8974560fd59..e2eaff9c5ca 100644 --- a/ql/ql/src/codeql_ql/style/DeadCodeQuery.qll +++ b/ql/ql/src/codeql_ql/style/DeadCodeQuery.qll @@ -203,7 +203,7 @@ private AstNode classUnion() { private AstNode benign() { not result.getLocation().getFile().getExtension() = ["ql", "qll"] or // ignore dbscheme files - result instanceof BlockComment or + result instanceof Comment or not exists(result.toString()) or // <- invalid code // cached-stages pattern result.(Module).getAMember().(ClasslessPredicate).getName() = "forceStage" or From 3e06455ac1e11ceedbb5ae2b18d54ceb6bacc921 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Thu, 14 Jul 2022 15:39:36 +0200 Subject: [PATCH 257/505] Swift: delete `TargetFile`'s move assignment --- swift/extractor/infra/TargetFile.cpp | 10 ---------- swift/extractor/infra/TargetFile.h | 3 ++- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/swift/extractor/infra/TargetFile.cpp b/swift/extractor/infra/TargetFile.cpp index d6c4df74318..c2639e81bd8 100644 --- a/swift/extractor/infra/TargetFile.cpp +++ b/swift/extractor/infra/TargetFile.cpp @@ -67,16 +67,6 @@ std::optional TargetFile::create(std::string_view target, return std::nullopt; } -TargetFile& TargetFile::operator=(TargetFile&& other) { - if (this != &other) { - commit(); - workingPath = std::move(other.workingPath); - targetPath = std::move(other.targetPath); - out = std::move(other.out); - } - return *this; -} - void TargetFile::commit() { if (out.is_open()) { out.close(); diff --git a/swift/extractor/infra/TargetFile.h b/swift/extractor/infra/TargetFile.h index 551abe0bb76..5f5ca7d823c 100644 --- a/swift/extractor/infra/TargetFile.h +++ b/swift/extractor/infra/TargetFile.h @@ -24,7 +24,8 @@ class TargetFile { ~TargetFile() { commit(); } TargetFile(TargetFile&& other) = default; - TargetFile& operator=(TargetFile&& other); + // move assignment deleted as non-trivial and not needed + TargetFile& operator=(TargetFile&& other) = delete; template TargetFile& operator<<(T&& value) { From 22ff8c2c7e33e2f6bc0533a2b99ea00044fb3cc5 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Thu, 14 Jul 2022 15:40:48 +0200 Subject: [PATCH 258/505] Swift: remove redundant braces --- swift/extractor/infra/TargetFile.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swift/extractor/infra/TargetFile.cpp b/swift/extractor/infra/TargetFile.cpp index c2639e81bd8..d9706205c90 100644 --- a/swift/extractor/infra/TargetFile.cpp +++ b/swift/extractor/infra/TargetFile.cpp @@ -63,7 +63,7 @@ std::optional TargetFile::create(std::string_view target, std::string_view targetDir, std::string_view workingDir) { TargetFile ret{target, targetDir, workingDir}; - if (ret.init()) return {std::move(ret)}; + if (ret.init()) return ret; return std::nullopt; } From d13f9d5d71e6de2e672a041f14460d5e8dd3184f Mon Sep 17 00:00:00 2001 From: Aditya Sharad <6874315+adityasharad@users.noreply.github.com> Date: Thu, 14 Jul 2022 07:29:29 -0700 Subject: [PATCH 259/505] Update docs/codeql/query-help/javascript.rst Co-authored-by: Felicity Chapman --- docs/codeql/query-help/javascript.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/codeql/query-help/javascript.rst b/docs/codeql/query-help/javascript.rst index ae85de99f7b..58fe97eb3b0 100644 --- a/docs/codeql/query-help/javascript.rst +++ b/docs/codeql/query-help/javascript.rst @@ -3,7 +3,7 @@ CodeQL query help for JavaScript .. include:: ../reusables/query-help-overview.rst -These queries are published in the CodeQL query pack ``codeql/javascript-queries`` (`changelog `__, `source `__). +These queries are published in the CodeQL query pack ``codeql/javascript-queries`` (`changelog `__, `source `__). For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository `__. From 5e74df3882b1d7e1f79cb1917f04114ff1608c3e Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Wed, 13 Jul 2022 19:05:05 +0200 Subject: [PATCH 260/505] Swift: cache file paths This required a bit of a generalization of `TrapLabelStore` to not work only with pointers. --- swift/extractor/SwiftExtractor.cpp | 22 ++---- swift/extractor/infra/FilePath.h | 24 ++++++ swift/extractor/infra/SwiftDispatcher.h | 99 +++++++++++++++---------- swift/extractor/infra/SwiftTagTraits.h | 17 +++-- swift/extractor/trap/TrapLabelStore.h | 6 +- swift/extractor/trap/TrapTagTraits.h | 3 +- swift/extractor/visitors/SwiftVisitor.h | 2 +- 7 files changed, 105 insertions(+), 68 deletions(-) create mode 100644 swift/extractor/infra/FilePath.h diff --git a/swift/extractor/SwiftExtractor.cpp b/swift/extractor/SwiftExtractor.cpp index 60ac3436fa8..666b7a90044 100644 --- a/swift/extractor/SwiftExtractor.cpp +++ b/swift/extractor/SwiftExtractor.cpp @@ -104,29 +104,19 @@ static void extractDeclarations(const SwiftExtractorConfiguration& config, dumpArgs(*trapTarget, config); TrapDomain trap{*trapTarget}; - // TODO: move default location emission elsewhere, possibly in a separate global trap file + // TODO: remove this and recreate it with IPA when we have that // the following cannot conflict with actual files as those have an absolute path starting with / - auto unknownFileLabel = trap.createLabel("unknown"); - auto unknownLocationLabel = trap.createLabel("unknown"); - trap.emit(FilesTrap{unknownFileLabel}); - trap.emit(LocationsTrap{unknownLocationLabel, unknownFileLabel}); + File unknownFileEntry{trap.createLabel("unknown")}; + Location unknownLocationEntry{trap.createLabel("unknown")}; + unknownLocationEntry.file = unknownFileEntry.id; + trap.emit(unknownFileEntry); + trap.emit(unknownLocationEntry); SwiftVisitor visitor(compiler.getSourceMgr(), trap, module, primaryFile); auto topLevelDecls = getTopLevelDecls(module, primaryFile); for (auto decl : topLevelDecls) { visitor.extract(decl); } - // TODO the following will be moved to the dispatcher when we start caching swift file objects - // for the moment, topLevelDecls always contains the current module, which does not have a file - // associated with it, so we need a special case when there are no top level declarations - if (topLevelDecls.size() == 1) { - // In the case of empty files, the dispatcher is not called, but we still want to 'record' the - // fact that the file was extracted - llvm::SmallString name(filename); - llvm::sys::fs::make_absolute(name); - auto fileLabel = trap.createLabel(name.str().str()); - trap.emit(FilesTrap{fileLabel, name.str().str()}); - } } static std::unordered_set collectInputFilenames(swift::CompilerInstance& compiler) { diff --git a/swift/extractor/infra/FilePath.h b/swift/extractor/infra/FilePath.h new file mode 100644 index 00000000000..48288b928e4 --- /dev/null +++ b/swift/extractor/infra/FilePath.h @@ -0,0 +1,24 @@ +#pragma once + +#include +namespace codeql { + +// wrapper around `std::string` mainly intended to unambiguously go into an `std::variant` +// TODO probably not needed once we can use `std::filesystem::path` +struct FilePath { + FilePath() = default; + FilePath(const std::string& path) : path{path} {} + FilePath(std::string&& path) : path{std::move(path)} {} + + std::string path; + + bool operator==(const FilePath& other) const { return path == other.path; } +}; +} // namespace codeql + +namespace std { +template <> +struct hash { + size_t operator()(const codeql::FilePath& value) { return hash{}(value.path); } +}; +} // namespace std diff --git a/swift/extractor/infra/SwiftDispatcher.h b/swift/extractor/infra/SwiftDispatcher.h index a9ecb6996ff..b0cc951dd31 100644 --- a/swift/extractor/infra/SwiftDispatcher.h +++ b/swift/extractor/infra/SwiftDispatcher.h @@ -8,6 +8,7 @@ #include "swift/extractor/trap/TrapDomain.h" #include "swift/extractor/infra/SwiftTagTraits.h" #include "swift/extractor/trap/generated/TrapClasses.h" +#include "swift/extractor/infra/FilePath.h" namespace codeql { @@ -17,6 +18,24 @@ namespace codeql { // Since SwiftDispatcher sees all the AST nodes, it also attaches a location to every 'locatable' // node (AST nodes that are not types: declarations, statements, expressions, etc.). class SwiftDispatcher { + // types to be supported by assignNewLabel/fetchLabel need to be listed here + using Store = TrapLabelStore; + + template + static constexpr bool IsStorable = std::is_constructible_v; + + template + static constexpr bool IsLocatable = std::is_base_of_v>; + public: // all references and pointers passed as parameters to this constructor are supposed to outlive // the SwiftDispatcher @@ -27,7 +46,12 @@ class SwiftDispatcher { : sourceManager{sourceManager}, trap{trap}, currentModule{currentModule}, - currentPrimarySourceFile{currentPrimarySourceFile} {} + currentPrimarySourceFile{currentPrimarySourceFile} { + if (currentPrimarySourceFile) { + // we make sure the file is in the trap output even if the source is empty + fetchLabel(getFilePath(currentPrimarySourceFile->getFilename())); + } + } template void emit(const Entry& entry) { @@ -61,9 +85,11 @@ class SwiftDispatcher { // This method gives a TRAP label for already emitted AST node. // If the AST node was not emitted yet, then the emission is dispatched to a corresponding // visitor (see `visit(T *)` methods below). - template - TrapLabelOf fetchLabel(E* e) { - assert(e && "trying to fetch a label on nullptr, maybe fetchOptionalLabel is to be used?"); + template >* = nullptr> + TrapLabelOf fetchLabel(const E& e) { + if constexpr (std::is_constructible_v) { + assert(e && "fetching a label on a null entity, maybe fetchOptionalLabel is to be used?"); + } // this is required so we avoid any recursive loop: a `fetchLabel` during the visit of `e` might // end up calling `fetchLabel` on `e` itself, so we want the visit of `e` to call `fetchLabel` // only after having called `assignNewLabel` on `e`. @@ -76,7 +102,7 @@ class SwiftDispatcher { visit(e); // TODO when everything is moved to structured C++ classes, this should be moved to createEntry if (auto l = store.get(e)) { - if constexpr (!std::is_base_of_v) { + if constexpr (IsLocatable) { attachLocation(e, *l); } return *l; @@ -93,15 +119,16 @@ class SwiftDispatcher { return fetchLabelFromUnion(node); } - TrapLabel fetchLabel(const swift::StmtConditionElement& element) { - return fetchLabel(&element); + template >* = nullptr> + TrapLabelOf fetchLabel(const E& e) { + return fetchLabel(&e); } // Due to the lazy emission approach, we must assign a label to a corresponding AST node before // it actually gets emitted to handle recursive cases such as recursive calls, or recursive type // declarations - template - TrapLabelOf assignNewLabel(E* e, Args&&... args) { + template >* = nullptr> + TrapLabelOf assignNewLabel(const E& e, Args&&... args) { assert(waitingForNewLabel == Store::Handle{e} && "assignNewLabel called on wrong entity"); auto label = trap.createLabel>(std::forward(args)...); store.insert(e, label); @@ -109,20 +136,20 @@ class SwiftDispatcher { return label; } - template >* = nullptr> + template >* = nullptr> TrapLabelOf assignNewLabel(const E& e, Args&&... args) { return assignNewLabel(&e, std::forward(args)...); } // convenience methods for structured C++ creation - template >* = nullptr> + template auto createEntry(const E& e, Args&&... args) { - return TrapClassOf{assignNewLabel(&e, std::forward(args)...)}; + return TrapClassOf{assignNewLabel(e, std::forward(args)...)}; } // used to create a new entry for entities that should not be cached // an example is swift::Argument, that are created on the fly and thus have no stable pointer - template >* = nullptr> + template auto createUncachedEntry(const E& e, Args&&... args) { auto label = trap.createLabel>(std::forward(args)...); attachLocation(&e, label); @@ -206,17 +233,6 @@ class SwiftDispatcher { } private: - // types to be supported by assignNewLabel/fetchLabel need to be listed here - using Store = TrapLabelStore; - void attachLocation(swift::SourceLoc start, swift::SourceLoc end, TrapLabel locatableLabel) { @@ -224,16 +240,16 @@ class SwiftDispatcher { // invalid locations seem to come from entities synthesized by the compiler return; } - std::string filepath = getFilepath(start); - auto fileLabel = trap.createLabel(filepath); - // TODO: do not emit duplicate trap entries for Files - trap.emit(FilesTrap{fileLabel, filepath}); - auto [startLine, startColumn] = sourceManager.getLineAndColumnInBuffer(start); - auto [endLine, endColumn] = sourceManager.getLineAndColumnInBuffer(end); - auto locLabel = trap.createLabel('{', fileLabel, "}:", startLine, ':', startColumn, - ':', endLine, ':', endColumn); - trap.emit(LocationsTrap{locLabel, fileLabel, startLine, startColumn, endLine, endColumn}); - trap.emit(LocatableLocationsTrap{locatableLabel, locLabel}); + auto file = getFilePath(sourceManager.getDisplayNameForLoc(start)); + Location entry{{}}; + entry.file = fetchLabel(file); + std::tie(entry.start_line, entry.start_column) = sourceManager.getLineAndColumnInBuffer(start); + std::tie(entry.end_line, entry.end_column) = sourceManager.getLineAndColumnInBuffer(end); + entry.id = trap.createLabel('{', entry.file, "}:", entry.start_line, ':', + entry.start_column, ':', entry.end_line, ':', + entry.end_column); + emit(entry); + emit(LocatableLocationsTrap{locatableLabel, entry.id}); } template @@ -256,21 +272,18 @@ class SwiftDispatcher { return false; } - std::string getFilepath(swift::SourceLoc loc) { + static FilePath getFilePath(llvm::StringRef path) { // TODO: this needs more testing // TODO: check canonicaliztion of names on a case insensitive filesystems // TODO: make symlink resolution conditional on CODEQL_PRESERVE_SYMLINKS=true - auto displayName = sourceManager.getDisplayNameForLoc(loc); llvm::SmallString realPath; - if (std::error_code ec = llvm::sys::fs::real_path(displayName, realPath)) { - std::cerr << "Cannot get real path: '" << displayName.str() << "': " << ec.message() << "\n"; + if (std::error_code ec = llvm::sys::fs::real_path(path, realPath)) { + std::cerr << "Cannot get real path: '" << path.str() << "': " << ec.message() << "\n"; return {}; } return realPath.str().str(); } - // TODO: The following methods are supposed to redirect TRAP emission to correpsonding visitors, - // which are to be introduced in follow-up PRs virtual void visit(swift::Decl* decl) = 0; virtual void visit(swift::Stmt* stmt) = 0; virtual void visit(const swift::StmtCondition* cond) = 0; @@ -281,6 +294,12 @@ class SwiftDispatcher { virtual void visit(swift::TypeRepr* type) = 0; virtual void visit(swift::TypeBase* type) = 0; + void visit(const FilePath& file) { + auto entry = createEntry(file); + entry.name = file.path; + emit(entry); + } + const swift::SourceManager& sourceManager; TrapDomain& trap; Store store; diff --git a/swift/extractor/infra/SwiftTagTraits.h b/swift/extractor/infra/SwiftTagTraits.h index 7447fa8f9b3..a4949b3ec5f 100644 --- a/swift/extractor/infra/SwiftTagTraits.h +++ b/swift/extractor/infra/SwiftTagTraits.h @@ -5,6 +5,7 @@ #include #include "swift/extractor/trap/TrapTagTraits.h" #include "swift/extractor/trap/generated/TrapTags.h" +#include "swift/extractor/infra/FilePath.h" namespace codeql { @@ -16,12 +17,12 @@ using SILFunctionTypeTag = SilFunctionTypeTag; using SILTokenTypeTag = SilTokenTypeTag; using SILBoxTypeReprTag = SilBoxTypeReprTag; -#define MAP_TYPE_TO_TAG(TYPE, TAG) \ - template <> \ - struct detail::ToTagFunctor { \ - using type = TAG; \ +#define MAP_TYPE_TO_TAG(TYPE, TAG) \ + template <> \ + struct detail::ToTagFunctor { \ + using type = TAG; \ } -#define MAP_TAG(TYPE) MAP_TYPE_TO_TAG(TYPE, TYPE##Tag) +#define MAP_TAG(TYPE) MAP_TYPE_TO_TAG(swift::TYPE, TYPE##Tag) #define MAP_SUBTAG(TYPE, PARENT) \ MAP_TAG(TYPE); \ static_assert(std::is_base_of_v, \ @@ -36,7 +37,7 @@ using SILBoxTypeReprTag = SilBoxTypeReprTag; MAP_TAG(Stmt); MAP_TAG(StmtCondition); -MAP_TYPE_TO_TAG(StmtConditionElement, ConditionElementTag); +MAP_TYPE_TO_TAG(swift::StmtConditionElement, ConditionElementTag); MAP_TAG(CaseLabelItem); #define ABSTRACT_STMT(CLASS, PARENT) MAP_SUBTAG(CLASS##Stmt, PARENT) #define STMT(CLASS, PARENT) ABSTRACT_STMT(CLASS, PARENT) @@ -63,7 +64,7 @@ MAP_TAG(TypeRepr); #define TYPEREPR(CLASS, PARENT) ABSTRACT_TYPEREPR(CLASS, PARENT) #include -MAP_TYPE_TO_TAG(TypeBase, TypeTag); +MAP_TYPE_TO_TAG(swift::TypeBase, TypeTag); #define ABSTRACT_TYPE(CLASS, PARENT) MAP_SUBTAG(CLASS##Type, PARENT) #define TYPE(CLASS, PARENT) ABSTRACT_TYPE(CLASS, PARENT) #include @@ -71,6 +72,8 @@ MAP_TYPE_TO_TAG(TypeBase, TypeTag); OVERRIDE_TAG(FuncDecl, ConcreteFuncDeclTag); OVERRIDE_TAG(VarDecl, ConcreteVarDeclTag); +MAP_TYPE_TO_TAG(FilePath, FileTag); + #undef MAP_TAG #undef MAP_SUBTAG #undef MAP_TYPE_TO_TAG diff --git a/swift/extractor/trap/TrapLabelStore.h b/swift/extractor/trap/TrapLabelStore.h index 93ca4212185..f9b0edd1753 100644 --- a/swift/extractor/trap/TrapLabelStore.h +++ b/swift/extractor/trap/TrapLabelStore.h @@ -20,10 +20,10 @@ namespace codeql { template class TrapLabelStore { public: - using Handle = std::variant; + using Handle = std::variant; template - std::optional> get(const T* e) { + std::optional> get(const T& e) { if (auto found = store_.find(e); found != store_.end()) { return TrapLabelOf::unsafeCreateFromUntyped(found->second); } @@ -31,7 +31,7 @@ class TrapLabelStore { } template - void insert(const T* e, TrapLabelOf l) { + void insert(const T& e, TrapLabelOf l) { auto [_, inserted] = store_.emplace(e, l); assert(inserted && "already inserted"); } diff --git a/swift/extractor/trap/TrapTagTraits.h b/swift/extractor/trap/TrapTagTraits.h index 9c29ff22330..f0f8c41cc8d 100644 --- a/swift/extractor/trap/TrapTagTraits.h +++ b/swift/extractor/trap/TrapTagTraits.h @@ -26,7 +26,8 @@ struct ToTrapClassFunctor; } // namespace detail template -using TrapTagOf = typename detail::ToTagOverride>::type; +using TrapTagOf = + typename detail::ToTagOverride>>::type; template using TrapLabelOf = TrapLabel>; diff --git a/swift/extractor/visitors/SwiftVisitor.h b/swift/extractor/visitors/SwiftVisitor.h index a13479246f4..d9d27f2a7ed 100644 --- a/swift/extractor/visitors/SwiftVisitor.h +++ b/swift/extractor/visitors/SwiftVisitor.h @@ -15,7 +15,7 @@ class SwiftVisitor : private SwiftDispatcher { using SwiftDispatcher::SwiftDispatcher; template - void extract(T* entity) { + void extract(const T& entity) { fetchLabel(entity); } From 0ee476129a847a52469fe639669b8ad0566f3fde Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 14 Jul 2022 14:38:49 +0000 Subject: [PATCH 261/505] Post-release preparation for codeql-cli-2.10.1 --- cpp/ql/lib/qlpack.yml | 2 +- cpp/ql/src/qlpack.yml | 2 +- csharp/ql/campaigns/Solorigate/lib/qlpack.yml | 2 +- csharp/ql/campaigns/Solorigate/src/qlpack.yml | 2 +- csharp/ql/lib/qlpack.yml | 2 +- csharp/ql/src/qlpack.yml | 2 +- go/ql/lib/qlpack.yml | 2 +- go/ql/src/qlpack.yml | 2 +- java/ql/lib/qlpack.yml | 2 +- java/ql/src/qlpack.yml | 2 +- javascript/ql/lib/qlpack.yml | 2 +- javascript/ql/src/qlpack.yml | 2 +- python/ql/lib/qlpack.yml | 2 +- python/ql/src/qlpack.yml | 2 +- ruby/ql/lib/qlpack.yml | 2 +- ruby/ql/src/qlpack.yml | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) diff --git a/cpp/ql/lib/qlpack.yml b/cpp/ql/lib/qlpack.yml index ae668b219fb..ce90251f83f 100644 --- a/cpp/ql/lib/qlpack.yml +++ b/cpp/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/cpp-all -version: 0.3.1 +version: 0.3.2-dev groups: cpp dbscheme: semmlecode.cpp.dbscheme extractor: cpp diff --git a/cpp/ql/src/qlpack.yml b/cpp/ql/src/qlpack.yml index 1a97e24c9c7..2735b4d5289 100644 --- a/cpp/ql/src/qlpack.yml +++ b/cpp/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/cpp-queries -version: 0.3.0 +version: 0.3.1-dev groups: - cpp - queries diff --git a/csharp/ql/campaigns/Solorigate/lib/qlpack.yml b/csharp/ql/campaigns/Solorigate/lib/qlpack.yml index 3b56b5ac98e..fc22389c2a8 100644 --- a/csharp/ql/campaigns/Solorigate/lib/qlpack.yml +++ b/csharp/ql/campaigns/Solorigate/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/csharp-solorigate-all -version: 1.2.1 +version: 1.2.2-dev groups: - csharp - solorigate diff --git a/csharp/ql/campaigns/Solorigate/src/qlpack.yml b/csharp/ql/campaigns/Solorigate/src/qlpack.yml index 54326ca55ad..a2ef81cc0e4 100644 --- a/csharp/ql/campaigns/Solorigate/src/qlpack.yml +++ b/csharp/ql/campaigns/Solorigate/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/csharp-solorigate-queries -version: 1.2.1 +version: 1.2.2-dev groups: - csharp - solorigate diff --git a/csharp/ql/lib/qlpack.yml b/csharp/ql/lib/qlpack.yml index 6aff1d2ac68..0d72cfc0c65 100644 --- a/csharp/ql/lib/qlpack.yml +++ b/csharp/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/csharp-all -version: 0.3.1 +version: 0.3.2-dev groups: csharp dbscheme: semmlecode.csharp.dbscheme extractor: csharp diff --git a/csharp/ql/src/qlpack.yml b/csharp/ql/src/qlpack.yml index b4e5b89773f..d3ceb328420 100644 --- a/csharp/ql/src/qlpack.yml +++ b/csharp/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/csharp-queries -version: 0.3.0 +version: 0.3.1-dev groups: - csharp - queries diff --git a/go/ql/lib/qlpack.yml b/go/ql/lib/qlpack.yml index 8f46c7040bb..c360e550193 100644 --- a/go/ql/lib/qlpack.yml +++ b/go/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/go-all -version: 0.2.1 +version: 0.2.2-dev groups: go dbscheme: go.dbscheme extractor: go diff --git a/go/ql/src/qlpack.yml b/go/ql/src/qlpack.yml index 3aff018b452..75ed3c98275 100644 --- a/go/ql/src/qlpack.yml +++ b/go/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/go-queries -version: 0.2.1 +version: 0.2.2-dev groups: - go - queries diff --git a/java/ql/lib/qlpack.yml b/java/ql/lib/qlpack.yml index 29b02e16b6e..0de218dcd22 100644 --- a/java/ql/lib/qlpack.yml +++ b/java/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/java-all -version: 0.3.1 +version: 0.3.2-dev groups: java dbscheme: config/semmlecode.dbscheme extractor: java diff --git a/java/ql/src/qlpack.yml b/java/ql/src/qlpack.yml index b0790498fe3..9cd3341f443 100644 --- a/java/ql/src/qlpack.yml +++ b/java/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/java-queries -version: 0.3.0 +version: 0.3.1-dev groups: - java - queries diff --git a/javascript/ql/lib/qlpack.yml b/javascript/ql/lib/qlpack.yml index cb4b01d90b0..9a05a09e0b6 100644 --- a/javascript/ql/lib/qlpack.yml +++ b/javascript/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/javascript-all -version: 0.2.1 +version: 0.2.2-dev groups: javascript dbscheme: semmlecode.javascript.dbscheme extractor: javascript diff --git a/javascript/ql/src/qlpack.yml b/javascript/ql/src/qlpack.yml index 07ac096af64..5525fe8b54b 100644 --- a/javascript/ql/src/qlpack.yml +++ b/javascript/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/javascript-queries -version: 0.3.0 +version: 0.3.1-dev groups: - javascript - queries diff --git a/python/ql/lib/qlpack.yml b/python/ql/lib/qlpack.yml index 7aac7c78527..f1a7c716b1e 100644 --- a/python/ql/lib/qlpack.yml +++ b/python/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/python-all -version: 0.5.1 +version: 0.5.2-dev groups: python dbscheme: semmlecode.python.dbscheme extractor: python diff --git a/python/ql/src/qlpack.yml b/python/ql/src/qlpack.yml index 6d07cdc1ab3..155e57024e8 100644 --- a/python/ql/src/qlpack.yml +++ b/python/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/python-queries -version: 0.3.0 +version: 0.3.1-dev groups: - python - queries diff --git a/ruby/ql/lib/qlpack.yml b/ruby/ql/lib/qlpack.yml index 5b67e5ccd50..8216fedd9d2 100644 --- a/ruby/ql/lib/qlpack.yml +++ b/ruby/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/ruby-all -version: 0.3.1 +version: 0.3.2-dev groups: ruby extractor: ruby dbscheme: ruby.dbscheme diff --git a/ruby/ql/src/qlpack.yml b/ruby/ql/src/qlpack.yml index 6a55e6cecd3..6715fc61912 100644 --- a/ruby/ql/src/qlpack.yml +++ b/ruby/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/ruby-queries -version: 0.3.0 +version: 0.3.1-dev groups: - ruby - queries From 625e37a0da4bfb057cd432ff3c31eadbee7bea48 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 14 Jul 2022 21:53:21 +0200 Subject: [PATCH 262/505] fix typo Co-authored-by: intrigus-lgtm <60750685+intrigus-lgtm@users.noreply.github.com> --- cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll index 74ba3569d20..79d9f85464e 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll @@ -699,7 +699,7 @@ private predicate exprToExprStep_nocfg(Expr fromExpr, Expr toExpr) { call.getTarget() = f and // AST dataflow treats a reference as if it were the referred-to object, while the dataflow // models treat references as pointers. If the return type of the call is a reference, then - // look for data flow the referred-to object, rather than the reference itself. + // look for data flow to the referred-to object, rather than the reference itself. if call.getType().getUnspecifiedType() instanceof ReferenceType then outModel.isReturnValueDeref() else outModel.isReturnValue() From 2f505491843462ca10128402d0dd39b27e9fe2d1 Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Fri, 15 Jul 2022 11:06:24 -0700 Subject: [PATCH 263/505] Move definitions.ql back to src --- go/ql/{lib => src}/definitions.ql | 0 javascript/ql/{lib => src}/definitions.ql | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename go/ql/{lib => src}/definitions.ql (100%) rename javascript/ql/{lib => src}/definitions.ql (100%) diff --git a/go/ql/lib/definitions.ql b/go/ql/src/definitions.ql similarity index 100% rename from go/ql/lib/definitions.ql rename to go/ql/src/definitions.ql diff --git a/javascript/ql/lib/definitions.ql b/javascript/ql/src/definitions.ql similarity index 100% rename from javascript/ql/lib/definitions.ql rename to javascript/ql/src/definitions.ql From b897a40228a8a9cf852680038e4b1ccc8be1ea6a Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Fri, 15 Jul 2022 12:06:41 -0700 Subject: [PATCH 264/505] Move python contextual queries to lib folders This will ensure that python projects can use jump to ref/def in vscode when the core libraries are not installed. --- python/ql/{src => lib}/analysis/LocalDefinitions.ql | 0 python/ql/{src => lib}/analysis/LocalReferences.ql | 0 .../src/change-notes/2022-07-15-move-contextual-queries.md | 5 +++++ 3 files changed, 5 insertions(+) rename python/ql/{src => lib}/analysis/LocalDefinitions.ql (100%) rename python/ql/{src => lib}/analysis/LocalReferences.ql (100%) create mode 100644 python/ql/src/change-notes/2022-07-15-move-contextual-queries.md diff --git a/python/ql/src/analysis/LocalDefinitions.ql b/python/ql/lib/analysis/LocalDefinitions.ql similarity index 100% rename from python/ql/src/analysis/LocalDefinitions.ql rename to python/ql/lib/analysis/LocalDefinitions.ql diff --git a/python/ql/src/analysis/LocalReferences.ql b/python/ql/lib/analysis/LocalReferences.ql similarity index 100% rename from python/ql/src/analysis/LocalReferences.ql rename to python/ql/lib/analysis/LocalReferences.ql diff --git a/python/ql/src/change-notes/2022-07-15-move-contextual-queries.md b/python/ql/src/change-notes/2022-07-15-move-contextual-queries.md new file mode 100644 index 00000000000..25ae1b57b99 --- /dev/null +++ b/python/ql/src/change-notes/2022-07-15-move-contextual-queries.md @@ -0,0 +1,5 @@ +--- +category: breaking +--- +* Contextual queries and the query libraries they depend on have been moved to the `codeql/python-all` package. + From fe789c8aa97b95be0fb49d8151f6a8816b6463a1 Mon Sep 17 00:00:00 2001 From: Raul Garcia <42392023+raulgarciamsft@users.noreply.github.com> Date: Sat, 16 Jul 2022 08:22:18 -0700 Subject: [PATCH 265/505] Update java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql Co-authored-by: yo-h <55373593+yo-h@users.noreply.github.com> --- .../CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql b/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql index ba78fa56423..5fac4f2c6c9 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql @@ -51,8 +51,8 @@ predicate isCreatingAzureClientSideEncryptionObjectNewVersion(Call call, Class c * A dataflow config that tracks `EncryptedBlobClientBuilder.version` argument initialization. */ private class EncryptedBlobClientBuilderSafeEncryptionVersionConfig extends DataFlow::Configuration { - EncryptedBlobClientBuilderEncryptionVersionConfig() { - this = "EncryptedBlobClientBuilderEncryptionVersionConfig" + EncryptedBlobClientBuilderSafeEncryptionVersionConfig() { + this = "EncryptedBlobClientBuilderSafeEncryptionVersionConfig" } override predicate isSource(DataFlow::Node source) { From eefa6595038ea4eba8abc6ab4efe513598954692 Mon Sep 17 00:00:00 2001 From: Raul Garcia <42392023+raulgarciamsft@users.noreply.github.com> Date: Sat, 16 Jul 2022 08:23:59 -0700 Subject: [PATCH 266/505] Update java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql Co-authored-by: yo-h <55373593+yo-h@users.noreply.github.com> --- .../CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql b/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql index 5fac4f2c6c9..287a3f07a6c 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql @@ -74,7 +74,7 @@ private class EncryptedBlobClientBuilderSafeEncryptionVersionConfig extends Data */ predicate isCreatingSafeAzureClientSideEncryptionObject(Call call, Class c, Expr versionArg) { isCreatingAzureClientSideEncryptionObjectNewVersion(call, c, versionArg) and - exists(EncryptedBlobClientBuilderEncryptionVersionConfig config, DataFlow::Node sink | + exists(EncryptedBlobClientBuilderSafeEncryptionVersionConfig config, DataFlow::Node sink | sink.asExpr() = versionArg | config.hasFlow(_, sink) From 6b17890e4f0adefdd7127197fd08857bc6296131 Mon Sep 17 00:00:00 2001 From: Raul Garcia Date: Sat, 16 Jul 2022 08:30:06 -0700 Subject: [PATCH 267/505] Fixing warning on usage of a deprecated feature. --- .../CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql b/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql index 3b35f2350bd..c9687b17821 100644 --- a/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql +++ b/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql @@ -33,7 +33,7 @@ predicate isUnsafeClientSideAzureStorageEncryptionViaAttributes(Call call, AttrN .getMember("BlobClient") .getReturn() .getMember("upload_blob") and - n.getAUse().asExpr() = a and + n.getAValueReachableFromSource().asExpr() = a and astmt.getATarget() = a and a.getAFlowNode() = node and uploadBlob = @@ -60,7 +60,7 @@ predicate isUnsafeClientSideAzureStorageEncryptionViaAttributes(Call call, AttrN .getMember("BlobClient") .getReturn() .getMember("encryption_version") and - encryptionVersion.getAUse().asExpr() = a2 and + encryptionVersion.getAValueReachableFromSource().asExpr() = a2 and astmt2.getATarget() = a2 and a2.getAFlowNode() = encryptionVersionSet and encryptionVersionSet.strictlyReaches(ctrlFlowNode) From dbd660787541d5f36264052d82910207fc2d3b01 Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Mon, 18 Jul 2022 08:54:58 +0100 Subject: [PATCH 268/505] Ruby: use ASCII dash in comment Co-authored-by: Harry Maclean --- ruby/ql/lib/codeql/ruby/frameworks/stdlib/Pathname.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruby/ql/lib/codeql/ruby/frameworks/stdlib/Pathname.qll b/ruby/ql/lib/codeql/ruby/frameworks/stdlib/Pathname.qll index e2cea924838..b6381c448ec 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/stdlib/Pathname.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/stdlib/Pathname.qll @@ -22,7 +22,7 @@ module Pathname { * puts pn.read * ``` * - * there are three `PathnameInstance`s – the call to `Pathname.new`, the + * there are three `PathnameInstance`s - the call to `Pathname.new`, the * assignment `pn = ...`, and the read access to `pn` on the second line. * * Every `PathnameInstance` is considered to be a `FileNameSource`. From 78fc356febfc28d4175b4881b664d796a8c37e94 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Mon, 18 Jul 2022 10:29:20 +0200 Subject: [PATCH 269/505] Swift: address review comments --- swift/extractor/infra/SwiftDispatcher.h | 7 ++++--- swift/extractor/visitors/ExprVisitor.cpp | 2 +- swift/extractor/visitors/ExprVisitor.h | 8 +++++--- swift/extractor/visitors/TypeVisitor.cpp | 2 ++ 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/swift/extractor/infra/SwiftDispatcher.h b/swift/extractor/infra/SwiftDispatcher.h index 095ddf0a7aa..baf3bdba292 100644 --- a/swift/extractor/infra/SwiftDispatcher.h +++ b/swift/extractor/infra/SwiftDispatcher.h @@ -78,7 +78,7 @@ class SwiftDispatcher { waitingForNewLabel = e; visit(e); if (auto l = store.get(e)) { - if constexpr (std::is_base_of_v>) { + if constexpr (!std::is_base_of_v) { attachLocation(e, *l); } return *l; @@ -285,8 +285,9 @@ class SwiftDispatcher { return realPath.str().str(); } - // TODO: The following methods are supposed to redirect TRAP emission to correpsonding visitors, - // which are to be introduced in follow-up PRs + // TODO: for const correctness these should consistently be `const` (and maybe const references + // as we don't expect `nullptr` here. However `swift::ASTVisitor` and `swift::TypeVisitor` do not + // accept const pointers virtual void visit(swift::Decl* decl) = 0; virtual void visit(const swift::IfConfigClause* clause) = 0; virtual void visit(swift::Stmt* stmt) = 0; diff --git a/swift/extractor/visitors/ExprVisitor.cpp b/swift/extractor/visitors/ExprVisitor.cpp index 90ca0a168ee..e1baf3a80dc 100644 --- a/swift/extractor/visitors/ExprVisitor.cpp +++ b/swift/extractor/visitors/ExprVisitor.cpp @@ -667,11 +667,11 @@ void ExprVisitor::emitLookupExpr(const swift::LookupExpr* expr, TrapLabel { codeql::BridgeFromObjCExpr translateBridgeFromObjCExpr(const swift::BridgeFromObjCExpr& expr); codeql::DotSelfExpr translateDotSelfExpr(const swift::DotSelfExpr& expr); codeql::ErrorExpr translateErrorExpr(const swift::ErrorExpr& expr); - // following requires non-const because: - // * `swift::UnresolvedPatternExpr::getSubPattern` gives `const swift::Pattern*` on const refs + // The following function requires a non-const parameter because: + // * `swift::UnresolvedPatternExpr::getSubPattern` has a `const`-qualified overload returning + // `const swift::Pattern*` // * `swift::ASTVisitor` only visits non-const pointers - // either we accept this, or we fix constness by providing our own const visiting in VisitorBase + // either we accept this, or we fix constness, e.g. by providing `visit` on `const` pointers + // in `VisitorBase`, or by doing a `const_cast` in `SwifDispatcher::fetchLabel` codeql::UnresolvedPatternExpr translateUnresolvedPatternExpr(swift::UnresolvedPatternExpr& expr); private: diff --git a/swift/extractor/visitors/TypeVisitor.cpp b/swift/extractor/visitors/TypeVisitor.cpp index 945ef5693ce..988e16b527c 100644 --- a/swift/extractor/visitors/TypeVisitor.cpp +++ b/swift/extractor/visitors/TypeVisitor.cpp @@ -1,4 +1,5 @@ #include "swift/extractor/visitors/TypeVisitor.h" + namespace codeql { void TypeVisitor::visit(swift::TypeBase* type) { TypeVisitorBase::visit(type); @@ -367,6 +368,7 @@ codeql::BuiltinVectorType TypeVisitor::translateBuiltinVectorType( const swift::BuiltinVectorType& type) { return createTypeEntry(type); } + codeql::OpenedArchetypeType TypeVisitor::translateOpenedArchetypeType( const swift::OpenedArchetypeType& type) { auto entry = createTypeEntry(type); From c08c3955d6f5ebe824eb9570edf3d463e4fd66f4 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Mon, 18 Jul 2022 10:37:54 +0200 Subject: [PATCH 270/505] Swift: add `UnresolvedPatternExpr` test --- swift/codegen/schema.yml | 1 - .../expr/UnresolvedPatternExpr/unresolved_pattern_expr.swift | 5 +++++ 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 swift/ql/test/extractor-tests/generated/expr/UnresolvedPatternExpr/unresolved_pattern_expr.swift diff --git a/swift/codegen/schema.yml b/swift/codegen/schema.yml index 540f187d021..13d01fefc0e 100644 --- a/swift/codegen/schema.yml +++ b/swift/codegen/schema.yml @@ -569,7 +569,6 @@ UnresolvedMemberExpr: UnresolvedPatternExpr: _extends: Expr - _pragma: qltest_skip # we should really never extract these _children: sub_pattern: Pattern diff --git a/swift/ql/test/extractor-tests/generated/expr/UnresolvedPatternExpr/unresolved_pattern_expr.swift b/swift/ql/test/extractor-tests/generated/expr/UnresolvedPatternExpr/unresolved_pattern_expr.swift new file mode 100644 index 00000000000..2ca1bbb7c24 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/expr/UnresolvedPatternExpr/unresolved_pattern_expr.swift @@ -0,0 +1,5 @@ +#if FOO +if case let .some(x) = 42 { + print(x) +} +#endif From c779936ee80e68ae324c3cd0a8ba98ec530c60e7 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Mon, 18 Jul 2022 11:19:40 +0200 Subject: [PATCH 271/505] Swift: commit forgotten files --- .../UnresolvedPatternExpr.expected | 1 + .../UnresolvedPatternExpr/UnresolvedPatternExpr.ql | 10 ++++++++++ .../UnresolvedPatternExpr_getType.expected | 0 .../UnresolvedPatternExpr_getType.ql | 7 +++++++ 4 files changed, 18 insertions(+) create mode 100644 swift/ql/test/extractor-tests/generated/expr/UnresolvedPatternExpr/UnresolvedPatternExpr.expected create mode 100644 swift/ql/test/extractor-tests/generated/expr/UnresolvedPatternExpr/UnresolvedPatternExpr.ql create mode 100644 swift/ql/test/extractor-tests/generated/expr/UnresolvedPatternExpr/UnresolvedPatternExpr_getType.expected create mode 100644 swift/ql/test/extractor-tests/generated/expr/UnresolvedPatternExpr/UnresolvedPatternExpr_getType.ql diff --git a/swift/ql/test/extractor-tests/generated/expr/UnresolvedPatternExpr/UnresolvedPatternExpr.expected b/swift/ql/test/extractor-tests/generated/expr/UnresolvedPatternExpr/UnresolvedPatternExpr.expected new file mode 100644 index 00000000000..3f348092bdb --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/expr/UnresolvedPatternExpr/UnresolvedPatternExpr.expected @@ -0,0 +1 @@ +| unresolved_pattern_expr.swift:2:19:2:19 | UnresolvedPatternExpr | getSubPattern: | unresolved_pattern_expr.swift:2:19:2:19 | x | diff --git a/swift/ql/test/extractor-tests/generated/expr/UnresolvedPatternExpr/UnresolvedPatternExpr.ql b/swift/ql/test/extractor-tests/generated/expr/UnresolvedPatternExpr/UnresolvedPatternExpr.ql new file mode 100644 index 00000000000..9a5f8e54490 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/expr/UnresolvedPatternExpr/UnresolvedPatternExpr.ql @@ -0,0 +1,10 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from UnresolvedPatternExpr x, Pattern getSubPattern +where + toBeTested(x) and + not x.isUnknown() and + getSubPattern = x.getSubPattern() +select x, "getSubPattern:", getSubPattern diff --git a/swift/ql/test/extractor-tests/generated/expr/UnresolvedPatternExpr/UnresolvedPatternExpr_getType.expected b/swift/ql/test/extractor-tests/generated/expr/UnresolvedPatternExpr/UnresolvedPatternExpr_getType.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/swift/ql/test/extractor-tests/generated/expr/UnresolvedPatternExpr/UnresolvedPatternExpr_getType.ql b/swift/ql/test/extractor-tests/generated/expr/UnresolvedPatternExpr/UnresolvedPatternExpr_getType.ql new file mode 100644 index 00000000000..af57d0129a1 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/expr/UnresolvedPatternExpr/UnresolvedPatternExpr_getType.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from UnresolvedPatternExpr x +where toBeTested(x) and not x.isUnknown() +select x, x.getType() From 52a9fb0de78fdd18efcfc146ab0a50363279f8c2 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Tue, 21 Jun 2022 14:26:28 +0200 Subject: [PATCH 272/505] C#: Add test for decrypt. --- .../HardcodedSymmetricEncryptionKey.cs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/csharp/ql/test/query-tests/Security Features/CWE-321/HardcodedSymmetricEncryptionKey/HardcodedSymmetricEncryptionKey.cs b/csharp/ql/test/query-tests/Security Features/CWE-321/HardcodedSymmetricEncryptionKey/HardcodedSymmetricEncryptionKey.cs index 23980864339..ec7280dfe73 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-321/HardcodedSymmetricEncryptionKey/HardcodedSymmetricEncryptionKey.cs +++ b/csharp/ql/test/query-tests/Security Features/CWE-321/HardcodedSymmetricEncryptionKey/HardcodedSymmetricEncryptionKey.cs @@ -46,6 +46,9 @@ namespace HardcodedSymmetricEncryptionKey // GOOD (this function hashes password) var de = DecryptWithPassword(ct, c, iv); + // BAD: harc-coded password passed to Decrypt + var de1 = Decrypt(ct, c, iv); + // BAD [NOT DETECTED] CreateCryptographicKey(null, byteArrayFromString); @@ -53,6 +56,26 @@ namespace HardcodedSymmetricEncryptionKey CreateCryptographicKey(null, File.ReadAllBytes("secret.key")); } + public static string Decrypt(byte[] cipherText, byte[] password, byte[] IV) + { + byte[] rawPlaintext; + var salt = new byte[] { 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00 }; + + using (Aes aes = new AesManaged()) + { + using (MemoryStream ms = new MemoryStream()) + { + using (CryptoStream cs = new CryptoStream(ms, aes.CreateDecryptor(password, IV), CryptoStreamMode.Write)) + { + cs.Write(cipherText, 0, cipherText.Length); + } + rawPlaintext = ms.ToArray(); + } + + return Encoding.Unicode.GetString(rawPlaintext); + } + } + public static string DecryptWithPassword(byte[] cipherText, byte[] password, byte[] IV) { byte[] rawPlaintext; From e6e82ef56d558583cd0beac40714d35f11182cdc Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Tue, 21 Jun 2022 14:28:40 +0200 Subject: [PATCH 273/505] C#: Update test with Decrypt example. --- .../HardcodedSymmetricEncryptionKey.expected | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/csharp/ql/test/query-tests/Security Features/CWE-321/HardcodedSymmetricEncryptionKey/HardcodedSymmetricEncryptionKey.expected b/csharp/ql/test/query-tests/Security Features/CWE-321/HardcodedSymmetricEncryptionKey/HardcodedSymmetricEncryptionKey.expected index fd9dd378fa5..676148bd7ed 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-321/HardcodedSymmetricEncryptionKey/HardcodedSymmetricEncryptionKey.expected +++ b/csharp/ql/test/query-tests/Security Features/CWE-321/HardcodedSymmetricEncryptionKey/HardcodedSymmetricEncryptionKey.expected @@ -1,6 +1,7 @@ | HardcodedSymmetricEncryptionKey.cs:17:21:17:97 | array creation of type Byte[] | Hard-coded symmetric $@ is used in symmetric algorithm in Key property assignment | HardcodedSymmetricEncryptionKey.cs:17:21:17:97 | array creation of type Byte[] | key | | HardcodedSymmetricEncryptionKey.cs:22:23:22:99 | array creation of type Byte[] | Hard-coded symmetric $@ is used in symmetric algorithm in Key property assignment | HardcodedSymmetricEncryptionKey.cs:22:23:22:99 | array creation of type Byte[] | key | | HardcodedSymmetricEncryptionKey.cs:31:21:31:21 | access to local variable d | Hard-coded symmetric $@ is used in symmetric algorithm in Key property assignment | HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] | key | -| HardcodedSymmetricEncryptionKey.cs:85:23:85:25 | access to parameter key | Hard-coded symmetric $@ is used in symmetric algorithm in Key property assignment | HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] | key | -| HardcodedSymmetricEncryptionKey.cs:98:87:98:89 | access to parameter key | Hard-coded symmetric $@ is used in symmetric algorithm in Encryptor(rgbKey, IV) | HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] | key | -| HardcodedSymmetricEncryptionKey.cs:98:87:98:89 | access to parameter key | Hard-coded symmetric $@ is used in symmetric algorithm in Encryptor(rgbKey, IV) | HardcodedSymmetricEncryptionKey.cs:28:62:28:115 | "Hello, world: here is a very bad way to create a key" | key | +| HardcodedSymmetricEncryptionKey.cs:68:87:68:94 | access to parameter password | Hard-coded symmetric $@ is used in symmetric algorithm in Decryptor(rgbKey, IV) | HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] | key | +| HardcodedSymmetricEncryptionKey.cs:108:23:108:25 | access to parameter key | Hard-coded symmetric $@ is used in symmetric algorithm in Key property assignment | HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] | key | +| HardcodedSymmetricEncryptionKey.cs:121:87:121:89 | access to parameter key | Hard-coded symmetric $@ is used in symmetric algorithm in Encryptor(rgbKey, IV) | HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] | key | +| HardcodedSymmetricEncryptionKey.cs:121:87:121:89 | access to parameter key | Hard-coded symmetric $@ is used in symmetric algorithm in Encryptor(rgbKey, IV) | HardcodedSymmetricEncryptionKey.cs:28:62:28:115 | "Hello, world: here is a very bad way to create a key" | key | From 383ad5168237b0ca79506ec1e3dee3a952bdaffe Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Tue, 21 Jun 2022 15:30:20 +0200 Subject: [PATCH 274/505] C#: Use CSV format for CreateEncryptor and CreateDecryptor sinks. --- .../HardcodedSymmetricEncryptionKey.qll | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/csharp/ql/lib/semmle/code/csharp/security/cryptography/HardcodedSymmetricEncryptionKey.qll b/csharp/ql/lib/semmle/code/csharp/security/cryptography/HardcodedSymmetricEncryptionKey.qll index 3cf3fa107bf..8d5c80699c8 100644 --- a/csharp/ql/lib/semmle/code/csharp/security/cryptography/HardcodedSymmetricEncryptionKey.qll +++ b/csharp/ql/lib/semmle/code/csharp/security/cryptography/HardcodedSymmetricEncryptionKey.qll @@ -4,6 +4,7 @@ */ import csharp +private import semmle.code.csharp.dataflow.ExternalFlow module HardcodedSymmetricEncryptionKey { private import semmle.code.csharp.frameworks.system.security.cryptography.SymmetricAlgorithm @@ -46,22 +47,24 @@ module HardcodedSymmetricEncryptionKey { override string getDescription() { result = "'Key' property assignment" } } - private class SymmetricEncryptionCreateEncryptorSink extends Sink { - SymmetricEncryptionCreateEncryptorSink() { - exists(SymmetricAlgorithm ag, MethodCall mc | mc = ag.getASymmetricEncryptor() | - this.asExpr() = mc.getArgumentForName("rgbKey") - ) + private class SymmetricAlgorithmCreateSinkCsv extends SinkModelCsv { + override predicate row(string row) { + row = + [ + "System.Security.Cryptography;SymmetricAlgorithm;true;CreateEncryptor;(System.Byte[],System.Byte[]);;Argument[0];encryption-encryptor", + "System.Security.Cryptography;SymmetricAlgorithm;true;CreateDecryptor;(System.Byte[],System.Byte[]);;Argument[0];encryption-decryptor" + ] } + } + + private class SymmetricAlgorithmCreateEncryptorSink extends Sink { + SymmetricAlgorithmCreateEncryptorSink() { sinkNode(this, "encryption-encryptor") } override string getDescription() { result = "Encryptor(rgbKey, IV)" } } - private class SymmetricEncryptionCreateDecryptorSink extends Sink { - SymmetricEncryptionCreateDecryptorSink() { - exists(SymmetricAlgorithm ag, MethodCall mc | mc = ag.getASymmetricDecryptor() | - this.asExpr() = mc.getArgumentForName("rgbKey") - ) - } + private class SymmetricAlgorithmCreateDecryptorSink extends Sink { + SymmetricAlgorithmCreateDecryptorSink() { sinkNode(this, "encryption-decryptor") } override string getDescription() { result = "Decryptor(rgbKey, IV)" } } From 1d405dba147099282916d9a103d43d0f475ddb75 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Tue, 21 Jun 2022 15:35:39 +0200 Subject: [PATCH 275/505] C#: Collapse Sink classes. --- .../HardcodedSymmetricEncryptionKey.qll | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/csharp/ql/lib/semmle/code/csharp/security/cryptography/HardcodedSymmetricEncryptionKey.qll b/csharp/ql/lib/semmle/code/csharp/security/cryptography/HardcodedSymmetricEncryptionKey.qll index 8d5c80699c8..557ae99810e 100644 --- a/csharp/ql/lib/semmle/code/csharp/security/cryptography/HardcodedSymmetricEncryptionKey.qll +++ b/csharp/ql/lib/semmle/code/csharp/security/cryptography/HardcodedSymmetricEncryptionKey.qll @@ -58,15 +58,15 @@ module HardcodedSymmetricEncryptionKey { } private class SymmetricAlgorithmCreateEncryptorSink extends Sink { - SymmetricAlgorithmCreateEncryptorSink() { sinkNode(this, "encryption-encryptor") } + private string kind; - override string getDescription() { result = "Encryptor(rgbKey, IV)" } - } + SymmetricAlgorithmCreateEncryptorSink() { sinkNode(this, kind) and kind.matches("encryption%") } - private class SymmetricAlgorithmCreateDecryptorSink extends Sink { - SymmetricAlgorithmCreateDecryptorSink() { sinkNode(this, "encryption-decryptor") } - - override string getDescription() { result = "Decryptor(rgbKey, IV)" } + override string getDescription() { + kind = "encryption-encryptor" and result = "Encryptor(rgbKey, IV)" + or + kind = "encryption-decryptor" and result = "Decryptor(rgbKey, IV)" + } } private class CreateSymmetricKeySink extends Sink { From 032448041d78e8cccfca6f39e69f96fd83f64a78 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Tue, 21 Jun 2022 15:51:33 +0200 Subject: [PATCH 276/505] C#: Convert CreateSymmetricKey to CSV sink. --- .../HardcodedSymmetricEncryptionKey.qll | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/csharp/ql/lib/semmle/code/csharp/security/cryptography/HardcodedSymmetricEncryptionKey.qll b/csharp/ql/lib/semmle/code/csharp/security/cryptography/HardcodedSymmetricEncryptionKey.qll index 557ae99810e..5fa17036f76 100644 --- a/csharp/ql/lib/semmle/code/csharp/security/cryptography/HardcodedSymmetricEncryptionKey.qll +++ b/csharp/ql/lib/semmle/code/csharp/security/cryptography/HardcodedSymmetricEncryptionKey.qll @@ -52,7 +52,8 @@ module HardcodedSymmetricEncryptionKey { row = [ "System.Security.Cryptography;SymmetricAlgorithm;true;CreateEncryptor;(System.Byte[],System.Byte[]);;Argument[0];encryption-encryptor", - "System.Security.Cryptography;SymmetricAlgorithm;true;CreateDecryptor;(System.Byte[],System.Byte[]);;Argument[0];encryption-decryptor" + "System.Security.Cryptography;SymmetricAlgorithm;true;CreateDecryptor;(System.Byte[],System.Byte[]);;Argument[0];encryption-decryptor", + "Windows.Security.Cryptography.Core;SymmetricKeyAlgorithmProvider;false;CreateSymmetricKey;(Windows.Storage.Streams.IBuffer);;Argument[0];encryption-symmetrickey" ] } } @@ -66,22 +67,11 @@ module HardcodedSymmetricEncryptionKey { kind = "encryption-encryptor" and result = "Encryptor(rgbKey, IV)" or kind = "encryption-decryptor" and result = "Decryptor(rgbKey, IV)" + or + kind = "encryption-symmetrickey" and result = "CreateSymmetricKey(IBuffer keyMaterial)" } } - private class CreateSymmetricKeySink extends Sink { - CreateSymmetricKeySink() { - exists(MethodCall mc, Method m | - mc.getTarget() = m and - m.hasQualifiedName("Windows.Security.Cryptography.Core.SymmetricKeyAlgorithmProvider", - "CreateSymmetricKey") and - this.asExpr() = mc.getArgumentForName("keyMaterial") - ) - } - - override string getDescription() { result = "CreateSymmetricKey(IBuffer keyMaterial)" } - } - private class CryptographicBuffer extends Class { CryptographicBuffer() { this.hasQualifiedName("Windows.Security.Cryptography", "CryptographicBuffer") From a5b7e2a2e1692e21173656c116c26d5eaa073991 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Tue, 21 Jun 2022 16:11:31 +0200 Subject: [PATCH 277/505] C#: Convert set Key of SymmetricAlgorithm to Csv sink. --- .../HardcodedSymmetricEncryptionKey.qll | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/csharp/ql/lib/semmle/code/csharp/security/cryptography/HardcodedSymmetricEncryptionKey.qll b/csharp/ql/lib/semmle/code/csharp/security/cryptography/HardcodedSymmetricEncryptionKey.qll index 5fa17036f76..cff9f254e1c 100644 --- a/csharp/ql/lib/semmle/code/csharp/security/cryptography/HardcodedSymmetricEncryptionKey.qll +++ b/csharp/ql/lib/semmle/code/csharp/security/cryptography/HardcodedSymmetricEncryptionKey.qll @@ -39,29 +39,22 @@ module HardcodedSymmetricEncryptionKey { StringLiteralSource() { this.asExpr() instanceof StringLiteral } } - private class SymmetricEncryptionKeyPropertySink extends Sink { - SymmetricEncryptionKeyPropertySink() { - this.asExpr() = any(SymmetricAlgorithm sa).getKeyProperty().getAnAssignedValue() - } - - override string getDescription() { result = "'Key' property assignment" } - } - - private class SymmetricAlgorithmCreateSinkCsv extends SinkModelCsv { + private class SymmetricAlgorithmSinkCsv extends SinkModelCsv { override predicate row(string row) { row = [ "System.Security.Cryptography;SymmetricAlgorithm;true;CreateEncryptor;(System.Byte[],System.Byte[]);;Argument[0];encryption-encryptor", "System.Security.Cryptography;SymmetricAlgorithm;true;CreateDecryptor;(System.Byte[],System.Byte[]);;Argument[0];encryption-decryptor", + "System.Security.Cryptography;SymmetricAlgorithm;true;set_Key;(System.Byte[]);;Argument[0];encryption-keyprop", "Windows.Security.Cryptography.Core;SymmetricKeyAlgorithmProvider;false;CreateSymmetricKey;(Windows.Storage.Streams.IBuffer);;Argument[0];encryption-symmetrickey" ] } } - private class SymmetricAlgorithmCreateEncryptorSink extends Sink { + private class SymmetricAlgorithmSink extends Sink { private string kind; - SymmetricAlgorithmCreateEncryptorSink() { sinkNode(this, kind) and kind.matches("encryption%") } + SymmetricAlgorithmSink() { sinkNode(this, kind) and kind.matches("encryption%") } override string getDescription() { kind = "encryption-encryptor" and result = "Encryptor(rgbKey, IV)" @@ -69,6 +62,8 @@ module HardcodedSymmetricEncryptionKey { kind = "encryption-decryptor" and result = "Decryptor(rgbKey, IV)" or kind = "encryption-symmetrickey" and result = "CreateSymmetricKey(IBuffer keyMaterial)" + or + kind = "encryption-keyprop" and result = "'Key' property assignment" } } From 66232a805409915191c8234474485e0fec19b506 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Wed, 22 Jun 2022 10:02:39 +0200 Subject: [PATCH 278/505] C#: Fix typo. --- .../HardcodedSymmetricEncryptionKey.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/csharp/ql/test/query-tests/Security Features/CWE-321/HardcodedSymmetricEncryptionKey/HardcodedSymmetricEncryptionKey.cs b/csharp/ql/test/query-tests/Security Features/CWE-321/HardcodedSymmetricEncryptionKey/HardcodedSymmetricEncryptionKey.cs index ec7280dfe73..0c9c58d0d23 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-321/HardcodedSymmetricEncryptionKey/HardcodedSymmetricEncryptionKey.cs +++ b/csharp/ql/test/query-tests/Security Features/CWE-321/HardcodedSymmetricEncryptionKey/HardcodedSymmetricEncryptionKey.cs @@ -46,7 +46,7 @@ namespace HardcodedSymmetricEncryptionKey // GOOD (this function hashes password) var de = DecryptWithPassword(ct, c, iv); - // BAD: harc-coded password passed to Decrypt + // BAD: hard-coded password passed to Decrypt var de1 = Decrypt(ct, c, iv); // BAD [NOT DETECTED] From c91d49a0fed77666c00ff870a72e9c947fa56640 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Wed, 22 Jun 2022 12:32:50 +0200 Subject: [PATCH 279/505] C#: Add provenance column to CSV format for SymmetricAlgorithm. --- .../cryptography/HardcodedSymmetricEncryptionKey.qll | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/csharp/ql/lib/semmle/code/csharp/security/cryptography/HardcodedSymmetricEncryptionKey.qll b/csharp/ql/lib/semmle/code/csharp/security/cryptography/HardcodedSymmetricEncryptionKey.qll index cff9f254e1c..79943481bbc 100644 --- a/csharp/ql/lib/semmle/code/csharp/security/cryptography/HardcodedSymmetricEncryptionKey.qll +++ b/csharp/ql/lib/semmle/code/csharp/security/cryptography/HardcodedSymmetricEncryptionKey.qll @@ -43,10 +43,10 @@ module HardcodedSymmetricEncryptionKey { override predicate row(string row) { row = [ - "System.Security.Cryptography;SymmetricAlgorithm;true;CreateEncryptor;(System.Byte[],System.Byte[]);;Argument[0];encryption-encryptor", - "System.Security.Cryptography;SymmetricAlgorithm;true;CreateDecryptor;(System.Byte[],System.Byte[]);;Argument[0];encryption-decryptor", - "System.Security.Cryptography;SymmetricAlgorithm;true;set_Key;(System.Byte[]);;Argument[0];encryption-keyprop", - "Windows.Security.Cryptography.Core;SymmetricKeyAlgorithmProvider;false;CreateSymmetricKey;(Windows.Storage.Streams.IBuffer);;Argument[0];encryption-symmetrickey" + "System.Security.Cryptography;SymmetricAlgorithm;true;CreateEncryptor;(System.Byte[],System.Byte[]);;Argument[0];encryption-encryptor;manual", + "System.Security.Cryptography;SymmetricAlgorithm;true;CreateDecryptor;(System.Byte[],System.Byte[]);;Argument[0];encryption-decryptor;manual", + "System.Security.Cryptography;SymmetricAlgorithm;true;set_Key;(System.Byte[]);;Argument[0];encryption-keyprop;manual", + "Windows.Security.Cryptography.Core;SymmetricKeyAlgorithmProvider;false;CreateSymmetricKey;(Windows.Storage.Streams.IBuffer);;Argument[0];encryption-symmetrickey;manual" ] } } From 57ba0c4e5d5e130beb44d4b676742e92b0af8dd5 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Mon, 18 Jul 2022 14:28:24 +0200 Subject: [PATCH 280/505] C#: Move sinks into System.Security.Cryptography framework code. --- .../system/security/Cryptography.qll | 20 +++++++++++++++++++ .../HardcodedSymmetricEncryptionKey.qll | 12 ----------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/csharp/ql/lib/semmle/code/csharp/frameworks/system/security/Cryptography.qll b/csharp/ql/lib/semmle/code/csharp/frameworks/system/security/Cryptography.qll index 156f99d7fde..aad7e601249 100644 --- a/csharp/ql/lib/semmle/code/csharp/frameworks/system/security/Cryptography.qll +++ b/csharp/ql/lib/semmle/code/csharp/frameworks/system/security/Cryptography.qll @@ -42,3 +42,23 @@ private class SystemSecurityCryptographyOidCollectionFlowModelCsv extends Summar ] } } + +/** Sinks for `System.Security.Cryptography`. */ +private class SystemSecurityCryptographySinkModelCsv extends SinkModelCsv { + override predicate row(string row) { + row = + [ + "System.Security.Cryptography;SymmetricAlgorithm;true;CreateEncryptor;(System.Byte[],System.Byte[]);;Argument[0];encryption-encryptor;manual", + "System.Security.Cryptography;SymmetricAlgorithm;true;CreateDecryptor;(System.Byte[],System.Byte[]);;Argument[0];encryption-decryptor;manual", + "System.Security.Cryptography;SymmetricAlgorithm;true;set_Key;(System.Byte[]);;Argument[0];encryption-keyprop;manual", + ] + } +} + +/** Sinks for `Windows.Security.Cryptography.Core`. */ +private class WindowsSecurityCryptographyCoreSinkModelCsv extends SinkModelCsv { + override predicate row(string row) { + row = + "Windows.Security.Cryptography.Core;SymmetricKeyAlgorithmProvider;false;CreateSymmetricKey;(Windows.Storage.Streams.IBuffer);;Argument[0];encryption-symmetrickey;manual" + } +} diff --git a/csharp/ql/lib/semmle/code/csharp/security/cryptography/HardcodedSymmetricEncryptionKey.qll b/csharp/ql/lib/semmle/code/csharp/security/cryptography/HardcodedSymmetricEncryptionKey.qll index 79943481bbc..e937e69919e 100644 --- a/csharp/ql/lib/semmle/code/csharp/security/cryptography/HardcodedSymmetricEncryptionKey.qll +++ b/csharp/ql/lib/semmle/code/csharp/security/cryptography/HardcodedSymmetricEncryptionKey.qll @@ -39,18 +39,6 @@ module HardcodedSymmetricEncryptionKey { StringLiteralSource() { this.asExpr() instanceof StringLiteral } } - private class SymmetricAlgorithmSinkCsv extends SinkModelCsv { - override predicate row(string row) { - row = - [ - "System.Security.Cryptography;SymmetricAlgorithm;true;CreateEncryptor;(System.Byte[],System.Byte[]);;Argument[0];encryption-encryptor;manual", - "System.Security.Cryptography;SymmetricAlgorithm;true;CreateDecryptor;(System.Byte[],System.Byte[]);;Argument[0];encryption-decryptor;manual", - "System.Security.Cryptography;SymmetricAlgorithm;true;set_Key;(System.Byte[]);;Argument[0];encryption-keyprop;manual", - "Windows.Security.Cryptography.Core;SymmetricKeyAlgorithmProvider;false;CreateSymmetricKey;(Windows.Storage.Streams.IBuffer);;Argument[0];encryption-symmetrickey;manual" - ] - } - } - private class SymmetricAlgorithmSink extends Sink { private string kind; From 6603024488f203492b2767fb9ba4ed0417b8d8b7 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Mon, 18 Jul 2022 14:32:31 +0200 Subject: [PATCH 281/505] C#: Allow encryption- prefix for sinks in CsvValidation. --- csharp/ql/lib/semmle/code/csharp/dataflow/ExternalFlow.qll | 1 + 1 file changed, 1 insertion(+) diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/ExternalFlow.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/ExternalFlow.qll index 782d33ea020..904ac79f009 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/ExternalFlow.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/ExternalFlow.qll @@ -377,6 +377,7 @@ module CsvValidation { exists(string row, string kind | sinkModel(row) | kind = row.splitAt(";", 7) and not kind = ["code", "sql", "xss", "remote", "html"] and + not kind.matches("encryption-%") and msg = "Invalid kind \"" + kind + "\" in sink model." ) or From 39fb714ad17e91cefbb3fa9a7160836ef8f3a303 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 18 Jul 2022 12:54:35 +0100 Subject: [PATCH 282/505] Swift: Add test with substring declared differently. --- .../CWE-135/StringLengthConflation.expected | 4 ++ .../CWE-135/StringLengthConflation2.swift | 42 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation2.swift diff --git a/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.expected b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.expected index 1af416dad2f..e5191b6446d 100644 --- a/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.expected +++ b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.expected @@ -1,4 +1,5 @@ edges +| StringLengthConflation2.swift:37:34:37:36 | .count : | StringLengthConflation2.swift:37:34:37:44 | ... call to -(_:_:) ... | | StringLengthConflation.swift:60:47:60:50 | .length : | StringLengthConflation.swift:60:47:60:59 | ... call to /(_:_:) ... | | StringLengthConflation.swift:66:33:66:36 | .length : | StringLengthConflation.swift:66:33:66:45 | ... call to /(_:_:) ... | | StringLengthConflation.swift:93:28:93:31 | .length : | StringLengthConflation.swift:93:28:93:40 | ... call to -(_:_:) ... | @@ -15,6 +16,8 @@ edges | StringLengthConflation.swift:135:36:135:38 | .count : | StringLengthConflation.swift:135:36:135:46 | ... call to -(_:_:) ... | | StringLengthConflation.swift:141:28:141:30 | .count : | StringLengthConflation.swift:141:28:141:38 | ... call to -(_:_:) ... | nodes +| StringLengthConflation2.swift:37:34:37:36 | .count : | semmle.label | .count : | +| StringLengthConflation2.swift:37:34:37:44 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | | StringLengthConflation.swift:53:43:53:46 | .length | semmle.label | .length | | StringLengthConflation.swift:60:47:60:50 | .length : | semmle.label | .length : | | StringLengthConflation.swift:60:47:60:59 | ... call to /(_:_:) ... | semmle.label | ... call to /(_:_:) ... | @@ -50,6 +53,7 @@ nodes | StringLengthConflation.swift:141:28:141:38 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | subpaths #select +| StringLengthConflation2.swift:37:34:37:44 | ... call to -(_:_:) ... | StringLengthConflation2.swift:37:34:37:36 | .count : | StringLengthConflation2.swift:37:34:37:44 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | | StringLengthConflation.swift:53:43:53:46 | .length | StringLengthConflation.swift:53:43:53:46 | .length | StringLengthConflation.swift:53:43:53:46 | .length | This NSString length is used in a String, but it may not be equivalent. | | StringLengthConflation.swift:60:47:60:59 | ... call to /(_:_:) ... | StringLengthConflation.swift:60:47:60:50 | .length : | StringLengthConflation.swift:60:47:60:59 | ... call to /(_:_:) ... | This NSString length is used in a String, but it may not be equivalent. | | StringLengthConflation.swift:66:33:66:45 | ... call to /(_:_:) ... | StringLengthConflation.swift:66:33:66:36 | .length : | StringLengthConflation.swift:66:33:66:45 | ... call to /(_:_:) ... | This NSString length is used in a String, but it may not be equivalent. | diff --git a/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation2.swift b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation2.swift new file mode 100644 index 00000000000..39b1456e604 --- /dev/null +++ b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation2.swift @@ -0,0 +1,42 @@ +// this test is in a separate file, because we want to test with a slightly different library +// implementation. In this version, some of the functions of `NSString` are in fact implemented +// in a base class `NSStringBase`. + +// --- stubs --- + +func print(_ items: Any...) {} + +typealias unichar = UInt16 + +class NSObject +{ +} + +class NSStringBase : NSObject +{ + func substring(from: Int) -> String { return "" } +} + +class NSString : NSStringBase +{ + init(string: String) { length = string.count } + + func substring(to: Int) -> String { return "" } + + private(set) var length: Int +} + +// --- tests --- + +func test(s: String) { + let ns = NSString(string: s) + + let nstr1 = ns.substring(from: ns.length - 1) // GOOD + let nstr2 = ns.substring(from: s.count - 1) // BAD: String length used in NSString [NOT DETECTED] + let nstr3 = ns.substring(to: ns.length - 1) // GOOD + let nstr4 = ns.substring(to: s.count - 1) // BAD: String length used in NSString + print("substrings '\(nstr1)' '\(nstr2)' / '\(nstr3)' '\(nstr4)'") +} + +// `begin :thumbsup: end`, with thumbs up emoji and skin tone modifier +test(s: "begin \u{0001F44D}\u{0001F3FF} end") From 4854679a4010f415e80151c4f797afff3e1f2ed0 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 18 Jul 2022 13:04:20 +0100 Subject: [PATCH 283/505] Swift: Clean up isSink (1 - move common variables to an outer exists). --- .../CWE-135/StringLengthConflation.ql | 163 +++++++++--------- 1 file changed, 81 insertions(+), 82 deletions(-) diff --git a/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql b/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql index 398c48f01e5..fa5c5f25e9d 100644 --- a/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql +++ b/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql @@ -41,88 +41,87 @@ class StringLengthConflationConfiguration extends DataFlow::Configuration { } override predicate isSink(DataFlow::Node node, string flowstate) { - // arguments to method calls... - exists( - string className, string methodName, string paramName, ClassDecl c, AbstractFunctionDecl f, - CallExpr call, int arg - | - ( - // `NSRange.init` - className = "NSRange" and - methodName = "init(location:length:)" and - paramName = ["location", "length"] - or - // `NSString.character` - className = ["NSString", "NSMutableString"] and - methodName = "character(at:)" and - paramName = "at" - or - // `NSString.character` - className = ["NSString", "NSMutableString"] and - methodName = "substring(from:)" and - paramName = "from" - or - // `NSString.character` - className = ["NSString", "NSMutableString"] and - methodName = "substring(to:)" and - paramName = "to" - or - // `NSMutableString.insert` - className = "NSMutableString" and - methodName = "insert(_:at:)" and - paramName = "at" - ) and - c.getName() = className and - c.getAMember() = f and // TODO: will this even work if its defined in a parent class? - call.getFunction().(ApplyExpr).getStaticTarget() = f and - f.getName() = methodName and - f.getParam(pragma[only_bind_into](arg)).getName() = paramName and - call.getArgument(pragma[only_bind_into](arg)).getExpr() = node.asExpr() and - flowstate = "String" // `String` length flowing into `NSString` - ) - or - // arguments to function calls... - exists(string funcName, string paramName, CallExpr call, int arg | - // `NSMakeRange` - funcName = "NSMakeRange(_:_:)" and - paramName = ["loc", "len"] and - call.getStaticTarget().getName() = funcName and - call.getStaticTarget().getParam(pragma[only_bind_into](arg)).getName() = paramName and - call.getArgument(pragma[only_bind_into](arg)).getExpr() = node.asExpr() and - flowstate = "String" // `String` length flowing into `NSString` - ) - or - // arguments to function calls... - exists(string funcName, string paramName, CallExpr call, int arg | - ( - // `String.dropFirst`, `String.dropLast`, `String.removeFirst`, `String.removeLast` - funcName = ["dropFirst(_:)", "dropLast(_:)", "removeFirst(_:)", "removeLast(_:)"] and - paramName = "k" - or - // `String.prefix`, `String.suffix` - funcName = ["prefix(_:)", "suffix(_:)"] and - paramName = "maxLength" - or - // `String.Index.init` - funcName = "init(encodedOffset:)" and - paramName = "offset" - or - // `String.index` - funcName = ["index(_:offsetBy:)", "index(_:offsetBy:limitBy:)"] and - paramName = "n" - or - // `String.formIndex` - funcName = ["formIndex(_:offsetBy:)", "formIndex(_:offsetBy:limitBy:)"] and - paramName = "distance" - ) and - call.getFunction().(ApplyExpr).getStaticTarget().getName() = funcName and - call.getFunction() - .(ApplyExpr) - .getStaticTarget() - .getParam(pragma[only_bind_into](arg)) - .getName() = paramName and - call.getArgument(pragma[only_bind_into](arg)).getExpr() = node.asExpr() and - flowstate = "NSString" // `NSString` length flowing into `String` + exists(CallExpr call, string paramName, int arg | + // arguments to method calls... + exists(string className, string methodName, ClassDecl c, AbstractFunctionDecl f | + ( + // `NSRange.init` + className = "NSRange" and + methodName = "init(location:length:)" and + paramName = ["location", "length"] + or + // `NSString.character` + className = ["NSString", "NSMutableString"] and + methodName = "character(at:)" and + paramName = "at" + or + // `NSString.character` + className = ["NSString", "NSMutableString"] and + methodName = "substring(from:)" and + paramName = "from" + or + // `NSString.character` + className = ["NSString", "NSMutableString"] and + methodName = "substring(to:)" and + paramName = "to" + or + // `NSMutableString.insert` + className = "NSMutableString" and + methodName = "insert(_:at:)" and + paramName = "at" + ) and + c.getName() = className and + c.getAMember() = f and // TODO: will this even work if its defined in a parent class? + call.getFunction().(ApplyExpr).getStaticTarget() = f and + f.getName() = methodName and + f.getParam(pragma[only_bind_into](arg)).getName() = paramName and + call.getArgument(pragma[only_bind_into](arg)).getExpr() = node.asExpr() and + flowstate = "String" // `String` length flowing into `NSString` + ) + or + // arguments to function calls... + exists(string funcName | + // `NSMakeRange` + funcName = "NSMakeRange(_:_:)" and + paramName = ["loc", "len"] and + call.getStaticTarget().getName() = funcName and + call.getStaticTarget().getParam(pragma[only_bind_into](arg)).getName() = paramName and + call.getArgument(pragma[only_bind_into](arg)).getExpr() = node.asExpr() and + flowstate = "String" // `String` length flowing into `NSString` + ) + or + // arguments to function calls... + exists(string funcName | + ( + // `String.dropFirst`, `String.dropLast`, `String.removeFirst`, `String.removeLast` + funcName = ["dropFirst(_:)", "dropLast(_:)", "removeFirst(_:)", "removeLast(_:)"] and + paramName = "k" + or + // `String.prefix`, `String.suffix` + funcName = ["prefix(_:)", "suffix(_:)"] and + paramName = "maxLength" + or + // `String.Index.init` + funcName = "init(encodedOffset:)" and + paramName = "offset" + or + // `String.index` + funcName = ["index(_:offsetBy:)", "index(_:offsetBy:limitBy:)"] and + paramName = "n" + or + // `String.formIndex` + funcName = ["formIndex(_:offsetBy:)", "formIndex(_:offsetBy:limitBy:)"] and + paramName = "distance" + ) and + call.getFunction().(ApplyExpr).getStaticTarget().getName() = funcName and + call.getFunction() + .(ApplyExpr) + .getStaticTarget() + .getParam(pragma[only_bind_into](arg)) + .getName() = paramName and + call.getArgument(pragma[only_bind_into](arg)).getExpr() = node.asExpr() and + flowstate = "NSString" // `NSString` length flowing into `String` + ) ) } From 0bd94a630761b0fdf103832f8f864407755b5790 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 18 Jul 2022 13:04:57 +0100 Subject: [PATCH 284/505] Swift: Clean up isSink (2 - rename methodName -> funcName and move that out as well). --- .../CWE-135/StringLengthConflation.ql | 92 +++++++++---------- 1 file changed, 44 insertions(+), 48 deletions(-) diff --git a/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql b/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql index fa5c5f25e9d..131e2b72ceb 100644 --- a/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql +++ b/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql @@ -41,87 +41,83 @@ class StringLengthConflationConfiguration extends DataFlow::Configuration { } override predicate isSink(DataFlow::Node node, string flowstate) { - exists(CallExpr call, string paramName, int arg | + exists(CallExpr call, string funcName, string paramName, int arg | // arguments to method calls... - exists(string className, string methodName, ClassDecl c, AbstractFunctionDecl f | + exists(string className, ClassDecl c, AbstractFunctionDecl f | ( // `NSRange.init` className = "NSRange" and - methodName = "init(location:length:)" and + funcName = "init(location:length:)" and paramName = ["location", "length"] or // `NSString.character` className = ["NSString", "NSMutableString"] and - methodName = "character(at:)" and + funcName = "character(at:)" and paramName = "at" or // `NSString.character` className = ["NSString", "NSMutableString"] and - methodName = "substring(from:)" and + funcName = "substring(from:)" and paramName = "from" or // `NSString.character` className = ["NSString", "NSMutableString"] and - methodName = "substring(to:)" and + funcName = "substring(to:)" and paramName = "to" or // `NSMutableString.insert` className = "NSMutableString" and - methodName = "insert(_:at:)" and + funcName = "insert(_:at:)" and paramName = "at" ) and c.getName() = className and c.getAMember() = f and // TODO: will this even work if its defined in a parent class? call.getFunction().(ApplyExpr).getStaticTarget() = f and - f.getName() = methodName and + f.getName() = funcName and f.getParam(pragma[only_bind_into](arg)).getName() = paramName and call.getArgument(pragma[only_bind_into](arg)).getExpr() = node.asExpr() and flowstate = "String" // `String` length flowing into `NSString` ) or // arguments to function calls... - exists(string funcName | - // `NSMakeRange` - funcName = "NSMakeRange(_:_:)" and - paramName = ["loc", "len"] and - call.getStaticTarget().getName() = funcName and - call.getStaticTarget().getParam(pragma[only_bind_into](arg)).getName() = paramName and - call.getArgument(pragma[only_bind_into](arg)).getExpr() = node.asExpr() and - flowstate = "String" // `String` length flowing into `NSString` - ) + // `NSMakeRange` + funcName = "NSMakeRange(_:_:)" and + paramName = ["loc", "len"] and + call.getStaticTarget().getName() = funcName and + call.getStaticTarget().getParam(pragma[only_bind_into](arg)).getName() = paramName and + call.getArgument(pragma[only_bind_into](arg)).getExpr() = node.asExpr() and + flowstate = "String" // `String` length flowing into `NSString` or // arguments to function calls... - exists(string funcName | - ( - // `String.dropFirst`, `String.dropLast`, `String.removeFirst`, `String.removeLast` - funcName = ["dropFirst(_:)", "dropLast(_:)", "removeFirst(_:)", "removeLast(_:)"] and - paramName = "k" - or - // `String.prefix`, `String.suffix` - funcName = ["prefix(_:)", "suffix(_:)"] and - paramName = "maxLength" - or - // `String.Index.init` - funcName = "init(encodedOffset:)" and - paramName = "offset" - or - // `String.index` - funcName = ["index(_:offsetBy:)", "index(_:offsetBy:limitBy:)"] and - paramName = "n" - or - // `String.formIndex` - funcName = ["formIndex(_:offsetBy:)", "formIndex(_:offsetBy:limitBy:)"] and - paramName = "distance" - ) and - call.getFunction().(ApplyExpr).getStaticTarget().getName() = funcName and - call.getFunction() - .(ApplyExpr) - .getStaticTarget() - .getParam(pragma[only_bind_into](arg)) - .getName() = paramName and - call.getArgument(pragma[only_bind_into](arg)).getExpr() = node.asExpr() and - flowstate = "NSString" // `NSString` length flowing into `String` - ) + ( + // `String.dropFirst`, `String.dropLast`, `String.removeFirst`, `String.removeLast` + funcName = ["dropFirst(_:)", "dropLast(_:)", "removeFirst(_:)", "removeLast(_:)"] and + paramName = "k" + or + // `String.prefix`, `String.suffix` + funcName = ["prefix(_:)", "suffix(_:)"] and + paramName = "maxLength" + or + // `String.Index.init` + funcName = "init(encodedOffset:)" and + paramName = "offset" + or + // `String.index` + funcName = ["index(_:offsetBy:)", "index(_:offsetBy:limitBy:)"] and + paramName = "n" + or + // `String.formIndex` + funcName = ["formIndex(_:offsetBy:)", "formIndex(_:offsetBy:limitBy:)"] and + paramName = "distance" + ) and + call.getFunction().(ApplyExpr).getStaticTarget().getName() = funcName and + call.getFunction() + .(ApplyExpr) + .getStaticTarget() + .getParam(pragma[only_bind_into](arg)) + .getName() = paramName and + call.getArgument(pragma[only_bind_into](arg)).getExpr() = node.asExpr() and + flowstate = "NSString" // `NSString` length flowing into `String` ) } From b136790efd53ba3488fc3a3c4b4c0b0f9749dbe7 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 18 Jul 2022 13:06:32 +0100 Subject: [PATCH 285/505] Swift: Clean up isSink (3 - rename f -> funcDecl and move that out as well; in the other two cases this variable didn't exist, now it does). --- .../CWE-135/StringLengthConflation.ql | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql b/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql index 131e2b72ceb..117cbac1e27 100644 --- a/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql +++ b/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql @@ -41,9 +41,11 @@ class StringLengthConflationConfiguration extends DataFlow::Configuration { } override predicate isSink(DataFlow::Node node, string flowstate) { - exists(CallExpr call, string funcName, string paramName, int arg | + exists( + AbstractFunctionDecl funcDecl, CallExpr call, string funcName, string paramName, int arg + | // arguments to method calls... - exists(string className, ClassDecl c, AbstractFunctionDecl f | + exists(string className, ClassDecl c | ( // `NSRange.init` className = "NSRange" and @@ -71,10 +73,10 @@ class StringLengthConflationConfiguration extends DataFlow::Configuration { paramName = "at" ) and c.getName() = className and - c.getAMember() = f and // TODO: will this even work if its defined in a parent class? - call.getFunction().(ApplyExpr).getStaticTarget() = f and - f.getName() = funcName and - f.getParam(pragma[only_bind_into](arg)).getName() = paramName and + c.getAMember() = funcDecl and // TODO: will this even work if its defined in a parent class? + call.getFunction().(ApplyExpr).getStaticTarget() = funcDecl and + funcDecl.getName() = funcName and + funcDecl.getParam(pragma[only_bind_into](arg)).getName() = paramName and call.getArgument(pragma[only_bind_into](arg)).getExpr() = node.asExpr() and flowstate = "String" // `String` length flowing into `NSString` ) @@ -83,8 +85,9 @@ class StringLengthConflationConfiguration extends DataFlow::Configuration { // `NSMakeRange` funcName = "NSMakeRange(_:_:)" and paramName = ["loc", "len"] and - call.getStaticTarget().getName() = funcName and - call.getStaticTarget().getParam(pragma[only_bind_into](arg)).getName() = paramName and + call.getStaticTarget() = funcDecl and + funcDecl.getName() = funcName and + funcDecl.getParam(pragma[only_bind_into](arg)).getName() = paramName and call.getArgument(pragma[only_bind_into](arg)).getExpr() = node.asExpr() and flowstate = "String" // `String` length flowing into `NSString` or @@ -110,12 +113,9 @@ class StringLengthConflationConfiguration extends DataFlow::Configuration { funcName = ["formIndex(_:offsetBy:)", "formIndex(_:offsetBy:limitBy:)"] and paramName = "distance" ) and - call.getFunction().(ApplyExpr).getStaticTarget().getName() = funcName and - call.getFunction() - .(ApplyExpr) - .getStaticTarget() - .getParam(pragma[only_bind_into](arg)) - .getName() = paramName and + call.getFunction().(ApplyExpr).getStaticTarget() = funcDecl and + funcDecl.getName() = funcName and + funcDecl.getParam(pragma[only_bind_into](arg)).getName() = paramName and call.getArgument(pragma[only_bind_into](arg)).getExpr() = node.asExpr() and flowstate = "NSString" // `NSString` length flowing into `String` ) From 9474e63faf23cda600ef3b1006dda2d4dfb31a15 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 18 Jul 2022 13:08:31 +0100 Subject: [PATCH 286/505] Swift: Clean up isSink (4 - move common code out). --- .../CWE-135/StringLengthConflation.ql | 132 +++++++++--------- 1 file changed, 64 insertions(+), 68 deletions(-) diff --git a/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql b/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql index 117cbac1e27..62e166487ec 100644 --- a/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql +++ b/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql @@ -44,80 +44,76 @@ class StringLengthConflationConfiguration extends DataFlow::Configuration { exists( AbstractFunctionDecl funcDecl, CallExpr call, string funcName, string paramName, int arg | - // arguments to method calls... - exists(string className, ClassDecl c | - ( - // `NSRange.init` - className = "NSRange" and - funcName = "init(location:length:)" and - paramName = ["location", "length"] - or - // `NSString.character` - className = ["NSString", "NSMutableString"] and - funcName = "character(at:)" and - paramName = "at" - or - // `NSString.character` - className = ["NSString", "NSMutableString"] and - funcName = "substring(from:)" and - paramName = "from" - or - // `NSString.character` - className = ["NSString", "NSMutableString"] and - funcName = "substring(to:)" and - paramName = "to" - or - // `NSMutableString.insert` - className = "NSMutableString" and - funcName = "insert(_:at:)" and - paramName = "at" - ) and - c.getName() = className and - c.getAMember() = funcDecl and // TODO: will this even work if its defined in a parent class? - call.getFunction().(ApplyExpr).getStaticTarget() = funcDecl and - funcDecl.getName() = funcName and - funcDecl.getParam(pragma[only_bind_into](arg)).getName() = paramName and - call.getArgument(pragma[only_bind_into](arg)).getExpr() = node.asExpr() and - flowstate = "String" // `String` length flowing into `NSString` - ) - or - // arguments to function calls... - // `NSMakeRange` - funcName = "NSMakeRange(_:_:)" and - paramName = ["loc", "len"] and - call.getStaticTarget() = funcDecl and - funcDecl.getName() = funcName and - funcDecl.getParam(pragma[only_bind_into](arg)).getName() = paramName and - call.getArgument(pragma[only_bind_into](arg)).getExpr() = node.asExpr() and - flowstate = "String" // `String` length flowing into `NSString` - or - // arguments to function calls... ( - // `String.dropFirst`, `String.dropLast`, `String.removeFirst`, `String.removeLast` - funcName = ["dropFirst(_:)", "dropLast(_:)", "removeFirst(_:)", "removeLast(_:)"] and - paramName = "k" + // arguments to method calls... + exists(string className, ClassDecl c | + ( + // `NSRange.init` + className = "NSRange" and + funcName = "init(location:length:)" and + paramName = ["location", "length"] + or + // `NSString.character` + className = ["NSString", "NSMutableString"] and + funcName = "character(at:)" and + paramName = "at" + or + // `NSString.character` + className = ["NSString", "NSMutableString"] and + funcName = "substring(from:)" and + paramName = "from" + or + // `NSString.character` + className = ["NSString", "NSMutableString"] and + funcName = "substring(to:)" and + paramName = "to" + or + // `NSMutableString.insert` + className = "NSMutableString" and + funcName = "insert(_:at:)" and + paramName = "at" + ) and + c.getName() = className and + c.getAMember() = funcDecl and // TODO: will this even work if its defined in a parent class? + call.getFunction().(ApplyExpr).getStaticTarget() = funcDecl and + flowstate = "String" // `String` length flowing into `NSString` + ) or - // `String.prefix`, `String.suffix` - funcName = ["prefix(_:)", "suffix(_:)"] and - paramName = "maxLength" + // arguments to function calls... + // `NSMakeRange` + funcName = "NSMakeRange(_:_:)" and + paramName = ["loc", "len"] and + call.getStaticTarget() = funcDecl and + flowstate = "String" // `String` length flowing into `NSString` or - // `String.Index.init` - funcName = "init(encodedOffset:)" and - paramName = "offset" - or - // `String.index` - funcName = ["index(_:offsetBy:)", "index(_:offsetBy:limitBy:)"] and - paramName = "n" - or - // `String.formIndex` - funcName = ["formIndex(_:offsetBy:)", "formIndex(_:offsetBy:limitBy:)"] and - paramName = "distance" + // arguments to function calls... + ( + // `String.dropFirst`, `String.dropLast`, `String.removeFirst`, `String.removeLast` + funcName = ["dropFirst(_:)", "dropLast(_:)", "removeFirst(_:)", "removeLast(_:)"] and + paramName = "k" + or + // `String.prefix`, `String.suffix` + funcName = ["prefix(_:)", "suffix(_:)"] and + paramName = "maxLength" + or + // `String.Index.init` + funcName = "init(encodedOffset:)" and + paramName = "offset" + or + // `String.index` + funcName = ["index(_:offsetBy:)", "index(_:offsetBy:limitBy:)"] and + paramName = "n" + or + // `String.formIndex` + funcName = ["formIndex(_:offsetBy:)", "formIndex(_:offsetBy:limitBy:)"] and + paramName = "distance" + ) and + call.getFunction().(ApplyExpr).getStaticTarget() = funcDecl and + flowstate = "NSString" // `NSString` length flowing into `String` ) and - call.getFunction().(ApplyExpr).getStaticTarget() = funcDecl and funcDecl.getName() = funcName and funcDecl.getParam(pragma[only_bind_into](arg)).getName() = paramName and - call.getArgument(pragma[only_bind_into](arg)).getExpr() = node.asExpr() and - flowstate = "NSString" // `NSString` length flowing into `String` + call.getArgument(pragma[only_bind_into](arg)).getExpr() = node.asExpr() ) } From 336548f746dedabeb5813821e7a4b05f7b8ce76b Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 18 Jul 2022 13:10:18 +0100 Subject: [PATCH 287/505] Swift: Improve comments. --- .../ql/src/queries/Security/CWE-135/StringLengthConflation.ql | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql b/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql index 62e166487ec..50001985055 100644 --- a/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql +++ b/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql @@ -86,7 +86,7 @@ class StringLengthConflationConfiguration extends DataFlow::Configuration { call.getStaticTarget() = funcDecl and flowstate = "String" // `String` length flowing into `NSString` or - // arguments to function calls... + // arguments to method calls... ( // `String.dropFirst`, `String.dropLast`, `String.removeFirst`, `String.removeLast` funcName = ["dropFirst(_:)", "dropLast(_:)", "removeFirst(_:)", "removeLast(_:)"] and @@ -111,6 +111,7 @@ class StringLengthConflationConfiguration extends DataFlow::Configuration { call.getFunction().(ApplyExpr).getStaticTarget() = funcDecl and flowstate = "NSString" // `NSString` length flowing into `String` ) and + // match up `funcName`, `paramName`, `arg`, `node`. funcDecl.getName() = funcName and funcDecl.getParam(pragma[only_bind_into](arg)).getName() = paramName and call.getArgument(pragma[only_bind_into](arg)).getExpr() = node.asExpr() From 541df9b5505eca66b5afe8aa1c91487d77672157 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 18 Jul 2022 14:26:08 +0100 Subject: [PATCH 288/505] Swift: Remove TODO comment. We have a test for this problem now. --- swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql b/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql index 50001985055..4e78d56f7d2 100644 --- a/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql +++ b/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql @@ -74,7 +74,7 @@ class StringLengthConflationConfiguration extends DataFlow::Configuration { paramName = "at" ) and c.getName() = className and - c.getAMember() = funcDecl and // TODO: will this even work if its defined in a parent class? + c.getAMember() = funcDecl and call.getFunction().(ApplyExpr).getStaticTarget() = funcDecl and flowstate = "String" // `String` length flowing into `NSString` ) From c9e5206396600f6ed11fec6bcd765a10696f4d8d Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Mon, 18 Jul 2022 15:26:38 +0200 Subject: [PATCH 289/505] Ruby: skip .git folder --- ruby/autobuilder/src/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/ruby/autobuilder/src/main.rs b/ruby/autobuilder/src/main.rs index 8f0f1b48d0d..ae4aa816190 100644 --- a/ruby/autobuilder/src/main.rs +++ b/ruby/autobuilder/src/main.rs @@ -19,6 +19,7 @@ fn main() -> std::io::Result<()> { .arg("--include-extension=.erb") .arg("--include-extension=.gemspec") .arg("--include=**/Gemfile") + .arg("--exclude=**/.git") .arg("--size-limit=5m") .arg("--language=ruby") .arg("--working-dir=.") From bdd771989f50d12f5b5323217b1c7598a00648b1 Mon Sep 17 00:00:00 2001 From: Taus Date: Mon, 18 Jul 2022 13:58:00 +0000 Subject: [PATCH 290/505] Python: Fix bad join in `syntactic_call_count` On certain databases, the evaluation of this predicate was running out of memory due to the way the `count` aggregate was being used. Here's an example of the tuple counts involved: ``` Tuple counts for PointsToContext::syntactic_call_count#cf3039a0#ff#antijoin_rhs/1@d2199bb8 after 1m27s: 595518502 ~521250% {1} r1 = JOIN PointsToContext::syntactic_call_count#cf3039a0#ff#shared#3 WITH Flow::CallNode::getFunction#dispred#f0820431#ff_1#join_rhs ON FIRST 1 OUTPUT Lhs.1 'arg0' 26518709 ~111513% {1} r2 = JOIN PointsToContext::syntactic_call_count#cf3039a0#ff#shared#2 WITH Flow::CallNode::getFunction#dispred#f0820431#ff_1#join_rhs ON FIRST 1 OUTPUT Lhs.1 'arg0' 622037211 ~498045% {1} r3 = r1 UNION r2 return r3 ``` and a timing report that looked like this: ``` time | evals | max @ iter | predicate ------|-------|--------------|---------- 5m8s | | | PointsToContext::syntactic_call_count#cf3039a0#ff#shared#2@6d98d1nd 4m38s | | | PointsToContext::syntactic_call_count#cf3039a0#ff#count_range@f5df1do4 3m51s | | | PointsToContext::syntactic_call_count#cf3039a0#ff#shared#3@da3b4abf 1m58s | 7613 | 37ms @ 4609 | MRO::ClassListList::removedClassParts#f0820431#fffff#reorder_2_3_4_0_1@8155axyi 1m37s | 7613 | 33ms @ 3904 | MRO::ClassListList::bestMergeCandidate#f0820431#2#fff@8155a83w 1m27s | | | PointsToContext::syntactic_call_count#cf3039a0#ff#antijoin_rhs@d2199bb8 1m8s | 1825 | 63ms @ 404 | PointsTo::Expressions::equalityEvaluatesTo#741b54e2#fffff@8155aw7w 37.6s | | | PointsToContext::syntactic_call_count#cf3039a0#ff#join_rhs@e348fc1p ... ``` To make optimising this easier for the compiler, I moved the bodies of the `count` aggregate into their own helper predicates (with size linear in the number of `CallNode`s), and also factored out the many calls to `f.getName()`. The astute reader will notice that in writing this as a sum of `count`s rather than a count of a disjunction, the intersection (if it exists) will be counted twice, and so the semantics may be different. However, since `method_call` and `function_call` require `AttrNode` and `NameNode` functions respectively, and as these two types are disjoint, there is no intersection, and so the semantics should be preserved. After the change, the evaluation of `syntactic_call_count` now looks as follows: ``` Tuple counts for PointsToContext::syntactic_call_count#cf3039a0#ff/2@662dd8s0 after 216ms: 23960 ~0% {1} r1 = @py_scope#f AND NOT py_Functions_0#antijoin_rhs(Lhs.0 's') 23960 ~0% {2} r2 = SCAN r1 OUTPUT In.0 's', 0 276309 ~7% {2} r3 = SCAN @py_scope#f OUTPUT In.0 's', "__init__" 11763 ~0% {2} r4 = JOIN r3 WITH Scope::Scope::getName#dispred#f0820431#fb ON FIRST 2 OUTPUT Lhs.0 's', 1 35723 ~0% {2} r5 = r2 UNION r4 252349 ~0% {2} r6 = JOIN @py_scope#f WITH Function::Function::getName#dispred#f0820431#ff ON FIRST 1 OUTPUT Lhs.0 's', Rhs.1 240586 ~0% {2} r7 = SELECT r6 ON In.1 != "__init__" 131727 ~4% {2} r8 = r7 AND NOT project#PointsToContext::method_call#cf3039a0#ff(Lhs.1) 131727 ~0% {3} r9 = SCAN r8 OUTPUT In.1, In.0 's', 0 240586 ~0% {2} r10 = SCAN r7 OUTPUT In.1, In.0 's' 108859 ~0% {3} r11 = JOIN r10 WITH PointsToContext::syntactic_call_count#cf3039a0#ff#join_rhs ON FIRST 1 OUTPUT Lhs.0, Lhs.1 's', Rhs.1 240586 ~0% {3} r12 = r9 UNION r11 24100 ~0% {2} r13 = JOIN r12 WITH PointsToContext::syntactic_call_count#cf3039a0#ff#join_rhs#1 ON FIRST 1 OUTPUT Lhs.1 's', (Rhs.1 + Lhs.2) 240586 ~0% {2} r14 = SELECT r6 ON In.1 != "__init__" 131727 ~4% {2} r15 = r14 AND NOT project#PointsToContext::method_call#cf3039a0#ff(Lhs.1) 131727 ~0% {3} r16 = SCAN r15 OUTPUT In.0 's', In.1, 0 108859 ~4% {3} r17 = JOIN r10 WITH PointsToContext::syntactic_call_count#cf3039a0#ff#join_rhs ON FIRST 1 OUTPUT Lhs.1 's', Lhs.0, Rhs.1 240586 ~4% {3} r18 = r16 UNION r17 216486 ~2% {3} r19 = r18 AND NOT project#PointsToContext::function_call#cf3039a0#ff(Lhs.1) 216486 ~0% {2} r20 = SCAN r19 OUTPUT In.0 's', (0 + In.2) 240586 ~0% {2} r21 = r13 UNION r20 276309 ~0% {2} r22 = r5 UNION r21 return r22 ``` --- .../semmle/python/pointsto/PointsToContext.qll | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/python/ql/lib/semmle/python/pointsto/PointsToContext.qll b/python/ql/lib/semmle/python/pointsto/PointsToContext.qll index 0e5f52b01b8..ad86a49ba6f 100644 --- a/python/ql/lib/semmle/python/pointsto/PointsToContext.qll +++ b/python/ql/lib/semmle/python/pointsto/PointsToContext.qll @@ -23,13 +23,8 @@ private int max_context_cost() { } private int syntactic_call_count(Scope s) { - exists(Function f | f = s and f.getName() != "__init__" | - result = - count(CallNode call | - call.getFunction().(NameNode).getId() = f.getName() - or - call.getFunction().(AttrNode).getName() = f.getName() - ) + exists(Function f, string name | f = s and name = f.getName() and name != "__init__" | + result = count(function_call(name)) + count(method_call(name)) ) or s.getName() = "__init__" and result = 1 @@ -37,6 +32,12 @@ private int syntactic_call_count(Scope s) { not s instanceof Function and result = 0 } +pragma[nomagic] +private CallNode function_call(string name) { result.getFunction().(NameNode).getId() = name } + +pragma[nomagic] +private CallNode method_call(string name) { result.getFunction().(AttrNode).getName() = name } + private int incoming_call_cost(Scope s) { /* * Syntactic call count will often be a considerable overestimate From f9b6ca76e5d124eb1881fdc1c5d3a686523bdce6 Mon Sep 17 00:00:00 2001 From: alexet Date: Mon, 18 Jul 2022 16:28:19 +0100 Subject: [PATCH 291/505] Python: Fix binding incorrect predicate. --- .../lib/semmle/python/frameworks/internal/SubclassFinder.qll | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/python/ql/lib/semmle/python/frameworks/internal/SubclassFinder.qll b/python/ql/lib/semmle/python/frameworks/internal/SubclassFinder.qll index 47521ac0b31..04b0a9e4afd 100644 --- a/python/ql/lib/semmle/python/frameworks/internal/SubclassFinder.qll +++ b/python/ql/lib/semmle/python/frameworks/internal/SubclassFinder.qll @@ -74,9 +74,7 @@ private module NotExposed { } /** DEPRECATED: Alias for fullyQualifiedToApiGraphPath */ - deprecated string fullyQualifiedToAPIGraphPath(string fullyQaulified) { - result = fullyQualifiedToApiGraphPath(fullyQaulified) - } + deprecated predicate fullyQualifiedToAPIGraphPath = fullyQualifiedToApiGraphPath/1; bindingset[this] abstract class FindSubclassesSpec extends string { From 7b8603c89b076fec2de4ecc474b1a43601d4ed98 Mon Sep 17 00:00:00 2001 From: Harry Maclean Date: Mon, 18 Jul 2022 15:05:17 +1200 Subject: [PATCH 292/505] Ruby: Model Arel.sql --- ruby/ql/lib/codeql/ruby/Frameworks.qll | 1 + ruby/ql/lib/codeql/ruby/frameworks/Arel.qll | 31 +++++++++++++++++++ .../frameworks/arel/Arel.expected | 3 ++ .../library-tests/frameworks/arel/Arel.ql | 11 +++++++ .../library-tests/frameworks/arel/arel.rb | 14 +++++++++ 5 files changed, 60 insertions(+) create mode 100644 ruby/ql/lib/codeql/ruby/frameworks/Arel.qll create mode 100644 ruby/ql/test/library-tests/frameworks/arel/Arel.expected create mode 100644 ruby/ql/test/library-tests/frameworks/arel/Arel.ql create mode 100644 ruby/ql/test/library-tests/frameworks/arel/arel.rb diff --git a/ruby/ql/lib/codeql/ruby/Frameworks.qll b/ruby/ql/lib/codeql/ruby/Frameworks.qll index 87b8a3f61a2..69ba758a767 100644 --- a/ruby/ql/lib/codeql/ruby/Frameworks.qll +++ b/ruby/ql/lib/codeql/ruby/Frameworks.qll @@ -10,6 +10,7 @@ private import codeql.ruby.frameworks.ActiveStorage private import codeql.ruby.frameworks.ActionView private import codeql.ruby.frameworks.ActiveSupport private import codeql.ruby.frameworks.Archive +private import codeql.ruby.frameworks.Arel private import codeql.ruby.frameworks.GraphQL private import codeql.ruby.frameworks.Rails private import codeql.ruby.frameworks.Railties diff --git a/ruby/ql/lib/codeql/ruby/frameworks/Arel.qll b/ruby/ql/lib/codeql/ruby/frameworks/Arel.qll new file mode 100644 index 00000000000..9fa17f4e5a5 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/frameworks/Arel.qll @@ -0,0 +1,31 @@ +/** + * Provides modeling for Arel, a low level SQL library that powers ActiveRecord. + * Version: 7.0.3 + * https://api.rubyonrails.org/classes/Arel.html + */ + +private import codeql.ruby.ApiGraphs +private import codeql.ruby.dataflow.FlowSummary + +/** + * Provides modeling for Arel, a low level SQL library that powers ActiveRecord. + * Version: 7.0.3 + * https://api.rubyonrails.org/classes/Arel.html + */ +module Arel { + /** + * Flow summary for `Arel.sql`. This method wraps a SQL string, marking it as + * safe. + */ + private class SqlSummary extends SummarizedCallable { + SqlSummary() { this = "Arel.sql" } + + override MethodCall getACall() { + result = API::getTopLevelMember("Arel").getAMethodCall("sql").asExpr().getExpr() + } + + override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { + input = "Argument[0]" and output = "ReturnValue" and preservesValue = false + } + } +} diff --git a/ruby/ql/test/library-tests/frameworks/arel/Arel.expected b/ruby/ql/test/library-tests/frameworks/arel/Arel.expected new file mode 100644 index 00000000000..63cd556e836 --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/arel/Arel.expected @@ -0,0 +1,3 @@ +failures +#select +| arel.rb:3:8:3:18 | call to sql | arel.rb:2:7:2:14 | call to source : | arel.rb:3:8:3:18 | call to sql | $@ | arel.rb:2:7:2:14 | call to source : | call to source : | diff --git a/ruby/ql/test/library-tests/frameworks/arel/Arel.ql b/ruby/ql/test/library-tests/frameworks/arel/Arel.ql new file mode 100644 index 00000000000..197a710803e --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/arel/Arel.ql @@ -0,0 +1,11 @@ +/** + * @kind path-problem + */ + +import codeql.ruby.frameworks.Arel +import ruby +import TestUtilities.InlineFlowTest + +from DataFlow::PathNode source, DataFlow::PathNode sink, DefaultTaintFlowConf conf +where conf.hasFlowPath(source, sink) +select sink, source, sink, "$@", source, source.toString() diff --git a/ruby/ql/test/library-tests/frameworks/arel/arel.rb b/ruby/ql/test/library-tests/frameworks/arel/arel.rb new file mode 100644 index 00000000000..7e7bc51f9a8 --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/arel/arel.rb @@ -0,0 +1,14 @@ +def m1 + x = source 1 + sink(Arel.sql(x)) # $hasTaintFlow=1 +end + +def m2 + x = 1 + sink(Arel.sql(x)) +end + +def m3 + x = source 1 + sink(Unrelated.method(x)) +end From 304203ad2f2cfeaafcc1dab46324937fb7415685 Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Tue, 19 Jul 2022 00:25:27 +0000 Subject: [PATCH 293/505] fix path problem output --- .../ManuallyCheckHttpVerb.ql | 6 +++--- .../ManuallyCheckHttpVerb.expected | 21 +++++++++++++------ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql b/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql index e7553b39a3d..354ccf62698 100644 --- a/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql +++ b/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql @@ -90,7 +90,7 @@ class HttpVerbConfig extends TaintTracking::Configuration { } } -from HttpVerbConfig config, DataFlow::Node source, DataFlow::Node sink -where config.hasFlow(source, sink) -select sink.asExpr().getExpr(), source, sink, +from HttpVerbConfig config, DataFlow::PathNode source, DataFlow::PathNode sink +where config.hasFlow(source.getNode(), sink.getNode()) +select sink.getNode(), source, sink, "Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods." diff --git a/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.expected b/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.expected index 9102005a67e..a253c3e9313 100644 --- a/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.expected +++ b/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.expected @@ -24,9 +24,18 @@ nodes subpaths #select | ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? | ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? | ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | -| ManuallyCheckHttpVerb.rb:12:8:12:22 | ... == ... | ManuallyCheckHttpVerb.rb:11:14:11:24 | call to env | ManuallyCheckHttpVerb.rb:12:8:12:22 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | -| ManuallyCheckHttpVerb.rb:20:8:20:22 | ... == ... | ManuallyCheckHttpVerb.rb:19:14:19:35 | call to request_method | ManuallyCheckHttpVerb.rb:20:8:20:22 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | -| ManuallyCheckHttpVerb.rb:28:8:28:22 | ... == ... | ManuallyCheckHttpVerb.rb:27:14:27:27 | call to method | ManuallyCheckHttpVerb.rb:28:8:28:22 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | -| ManuallyCheckHttpVerb.rb:36:8:36:22 | ... == ... | ManuallyCheckHttpVerb.rb:35:14:35:39 | call to raw_request_method | ManuallyCheckHttpVerb.rb:36:8:36:22 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | -| ManuallyCheckHttpVerb.rb:52:10:52:23 | ... == ... | ManuallyCheckHttpVerb.rb:51:16:51:44 | call to request_method_symbol | ManuallyCheckHttpVerb.rb:52:10:52:23 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | -| ManuallyCheckHttpVerb.rb:59:10:59:38 | ...[...] | ManuallyCheckHttpVerb.rb:59:10:59:20 | call to env | ManuallyCheckHttpVerb.rb:59:10:59:38 | ...[...] | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | +| ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? | ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? | ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? : | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | +| ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? | ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? : | ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | +| ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? | ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? : | ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? : | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | +| ManuallyCheckHttpVerb.rb:12:8:12:22 | ... == ... | ManuallyCheckHttpVerb.rb:11:14:11:24 | call to env : | ManuallyCheckHttpVerb.rb:12:8:12:22 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | +| ManuallyCheckHttpVerb.rb:12:8:12:22 | ... == ... | ManuallyCheckHttpVerb.rb:11:14:11:24 | call to env : | ManuallyCheckHttpVerb.rb:12:8:12:22 | ... == ... : | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | +| ManuallyCheckHttpVerb.rb:20:8:20:22 | ... == ... | ManuallyCheckHttpVerb.rb:19:14:19:35 | call to request_method : | ManuallyCheckHttpVerb.rb:20:8:20:22 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | +| ManuallyCheckHttpVerb.rb:20:8:20:22 | ... == ... | ManuallyCheckHttpVerb.rb:19:14:19:35 | call to request_method : | ManuallyCheckHttpVerb.rb:20:8:20:22 | ... == ... : | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | +| ManuallyCheckHttpVerb.rb:28:8:28:22 | ... == ... | ManuallyCheckHttpVerb.rb:27:14:27:27 | call to method : | ManuallyCheckHttpVerb.rb:28:8:28:22 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | +| ManuallyCheckHttpVerb.rb:28:8:28:22 | ... == ... | ManuallyCheckHttpVerb.rb:27:14:27:27 | call to method : | ManuallyCheckHttpVerb.rb:28:8:28:22 | ... == ... : | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | +| ManuallyCheckHttpVerb.rb:36:8:36:22 | ... == ... | ManuallyCheckHttpVerb.rb:35:14:35:39 | call to raw_request_method : | ManuallyCheckHttpVerb.rb:36:8:36:22 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | +| ManuallyCheckHttpVerb.rb:36:8:36:22 | ... == ... | ManuallyCheckHttpVerb.rb:35:14:35:39 | call to raw_request_method : | ManuallyCheckHttpVerb.rb:36:8:36:22 | ... == ... : | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | +| ManuallyCheckHttpVerb.rb:52:10:52:23 | ... == ... | ManuallyCheckHttpVerb.rb:51:16:51:44 | call to request_method_symbol : | ManuallyCheckHttpVerb.rb:52:10:52:23 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | +| ManuallyCheckHttpVerb.rb:52:10:52:23 | ... == ... | ManuallyCheckHttpVerb.rb:51:16:51:44 | call to request_method_symbol : | ManuallyCheckHttpVerb.rb:52:10:52:23 | ... == ... : | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | +| ManuallyCheckHttpVerb.rb:59:10:59:38 | ...[...] | ManuallyCheckHttpVerb.rb:59:10:59:20 | call to env : | ManuallyCheckHttpVerb.rb:59:10:59:38 | ...[...] | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | +| ManuallyCheckHttpVerb.rb:59:10:59:38 | ...[...] | ManuallyCheckHttpVerb.rb:59:10:59:20 | call to env : | ManuallyCheckHttpVerb.rb:59:10:59:38 | ...[...] : | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | From 9586259706b1c855d7426a8c54aef3da8422355a Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Tue, 19 Jul 2022 00:29:30 +0000 Subject: [PATCH 294/505] style tweak for checking multiple method names --- ruby/ql/src/experimental/weak-params/WeakParams.ql | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/ruby/ql/src/experimental/weak-params/WeakParams.ql b/ruby/ql/src/experimental/weak-params/WeakParams.ql index 86ba72cbd91..0f36d4930d3 100644 --- a/ruby/ql/src/experimental/weak-params/WeakParams.ql +++ b/ruby/ql/src/experimental/weak-params/WeakParams.ql @@ -38,13 +38,8 @@ class ActionControllerRequest extends DataFlow::Node { class WeakParams extends DataFlow::CallNode { WeakParams() { this.getReceiver() instanceof ActionControllerRequest and - ( - this.getMethodName() = "path_parametes" or - this.getMethodName() = "query_parameters" or - this.getMethodName() = "request_parameters" or - this.getMethodName() = "GET" or - this.getMethodName() = "POST" - ) + this.getMethodName() = + ["path_parametes", "query_parameters", "request_parameters", "GET", "POST"] } } From 962155fd61596271a83003d05ae25cdc365b0488 Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Tue, 19 Jul 2022 00:33:04 +0000 Subject: [PATCH 295/505] fix changenotes --- ...=> 2022-07-18-sqli-in-activerecord-relation-annotate.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename ruby/ql/lib/change-notes/{released/0.3.1.md => 2022-07-18-sqli-in-activerecord-relation-annotate.md} (64%) diff --git a/ruby/ql/lib/change-notes/released/0.3.1.md b/ruby/ql/lib/change-notes/2022-07-18-sqli-in-activerecord-relation-annotate.md similarity index 64% rename from ruby/ql/lib/change-notes/released/0.3.1.md rename to ruby/ql/lib/change-notes/2022-07-18-sqli-in-activerecord-relation-annotate.md index 392aa6f0b27..60ab137f8b2 100644 --- a/ruby/ql/lib/change-notes/released/0.3.1.md +++ b/ruby/ql/lib/change-notes/2022-07-18-sqli-in-activerecord-relation-annotate.md @@ -1,5 +1,5 @@ -## 0.3.1 - -### Minor Analysis Improvements +--- +category: minorAnalysis +--- - Calls to `ActiveRecord::Relation#annotate` are now recognized as`SqlExecution`s so that it will be considered as a sink for queries like rb/sql-injection. \ No newline at end of file From ec1d1eb54744cbfcd01f144be71fe3c311f3a061 Mon Sep 17 00:00:00 2001 From: Harry Maclean Date: Tue, 19 Jul 2022 14:33:51 +1200 Subject: [PATCH 296/505] Ruby: Add change note --- ruby/ql/lib/change-notes/2022-07-19-arel.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 ruby/ql/lib/change-notes/2022-07-19-arel.md diff --git a/ruby/ql/lib/change-notes/2022-07-19-arel.md b/ruby/ql/lib/change-notes/2022-07-19-arel.md new file mode 100644 index 00000000000..3dda3d4b1f6 --- /dev/null +++ b/ruby/ql/lib/change-notes/2022-07-19-arel.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Calls to `Arel.sql` are now recognised as propagating taint from their argument. From 0828474192694f508eb0a0b30326cf49e93b068a Mon Sep 17 00:00:00 2001 From: Henti Smith Date: Tue, 19 Jul 2022 15:34:10 +0100 Subject: [PATCH 297/505] Added Workflow::getName and Step::GetId --- javascript/ql/lib/semmle/javascript/Actions.qll | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/javascript/ql/lib/semmle/javascript/Actions.qll b/javascript/ql/lib/semmle/javascript/Actions.qll index f5b2c39b064..8e842c81e73 100644 --- a/javascript/ql/lib/semmle/javascript/Actions.qll +++ b/javascript/ql/lib/semmle/javascript/Actions.qll @@ -28,6 +28,9 @@ module Actions { /** Gets the `jobs` mapping from job IDs to job definitions in this workflow. */ YAMLMapping getJobs() { result = this.lookup("jobs") } + /** Gets the name of the workflow */ + string getName() { result = this.lookup("name").(YAMLString).getValue() } + /** Gets the name of the workflow file. */ string getFileName() { result = this.getFile().getBaseName() } @@ -129,6 +132,9 @@ module Actions { /** Gets the value of the `if` field in this step, if any. */ StepIf getIf() { result.getStep() = this } + + /** Gets the id of the step field, if any. */ + string getId() { result = this.lookup("id").(YAMLString).getValue() } } /** From dcc76ddf3629e8f579c68440a8aa20d67a90e656 Mon Sep 17 00:00:00 2001 From: Henti Smith <28868601+henti@users.noreply.github.com> Date: Tue, 19 Jul 2022 15:53:12 +0100 Subject: [PATCH 298/505] Apply suggestions from code review Co-authored-by: Henry Mercer --- javascript/ql/lib/semmle/javascript/Actions.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/Actions.qll b/javascript/ql/lib/semmle/javascript/Actions.qll index 8e842c81e73..8ea789c96e0 100644 --- a/javascript/ql/lib/semmle/javascript/Actions.qll +++ b/javascript/ql/lib/semmle/javascript/Actions.qll @@ -28,7 +28,7 @@ module Actions { /** Gets the `jobs` mapping from job IDs to job definitions in this workflow. */ YAMLMapping getJobs() { result = this.lookup("jobs") } - /** Gets the name of the workflow */ + /** Gets the name of the workflow. */ string getName() { result = this.lookup("name").(YAMLString).getValue() } /** Gets the name of the workflow file. */ @@ -133,7 +133,7 @@ module Actions { /** Gets the value of the `if` field in this step, if any. */ StepIf getIf() { result.getStep() = this } - /** Gets the id of the step field, if any. */ + /** Gets the ID of this step, if any. */ string getId() { result = this.lookup("id").(YAMLString).getValue() } } From 7620a6f6539dcbdf4efab6bd9a08335023c4d95a Mon Sep 17 00:00:00 2001 From: Aditya Sharad Date: Thu, 14 Jul 2022 12:08:22 -0700 Subject: [PATCH 299/505] Docs: Update supported languages page with links to CLI and pack information Include links to the CLI changelog, CLI releases, bundle releases, pack changelogs, and pack source. Clarify that this support information applies to the current version of the CLI, bundle, query packs, and library packs. --- .../supported-languages-and-frameworks.rst | 7 ++++-- docs/codeql/support/reusables/frameworks.rst | 24 +++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/docs/codeql/codeql-overview/supported-languages-and-frameworks.rst b/docs/codeql/codeql-overview/supported-languages-and-frameworks.rst index 4353f9402b7..be66125846a 100644 --- a/docs/codeql/codeql-overview/supported-languages-and-frameworks.rst +++ b/docs/codeql/codeql-overview/supported-languages-and-frameworks.rst @@ -11,14 +11,17 @@ CodeQL. Languages and compilers ####################### -CodeQL supports the following languages and compilers. +The current versions of the CodeQL CLI (`changelog `__, `releases `__), +CodeQL library packs (`source `__), +and CodeQL bundle (`releases `__) +support the following languages and compilers. .. include:: ../support/reusables/versions-compilers.rst Frameworks and libraries ######################## -The libraries and queries in the current version of CodeQL have been explicitly checked against the libraries and frameworks listed below. +The current versions of the CodeQL library and query packs (`source `__) have been explicitly checked against the libraries and frameworks listed below. .. pull-quote:: diff --git a/docs/codeql/support/reusables/frameworks.rst b/docs/codeql/support/reusables/frameworks.rst index 12bcd5af8e6..fc5410648cf 100644 --- a/docs/codeql/support/reusables/frameworks.rst +++ b/docs/codeql/support/reusables/frameworks.rst @@ -1,6 +1,10 @@ C and C++ built-in support ================================ +Provided by the current versions of the +CodeQL query pack ``codeql/cpp-queries`` (`changelog `__, `source `__) +and the CodeQL library pack ``codeql/cpp-all`` (`changelog `__, `source `__). + .. csv-table:: :header-rows: 1 :class: fullWidthTable @@ -14,6 +18,10 @@ C and C++ built-in support C# built-in support ================================ +Provided by the current versions of the +CodeQL query pack ``codeql/csharp-queries`` (`changelog `__, `source `__) +and the CodeQL library pack ``codeql/csharp-all`` (`changelog `__, `source `__). + .. csv-table:: :header-rows: 1 :class: fullWidthTable @@ -33,6 +41,10 @@ C# built-in support Go built-in support ================================ +Provided by the current versions of the +CodeQL query pack ``codeql/go-queries`` (`changelog `__, `source `__) +and the CodeQL library pack ``codeql/go-all`` (`changelog `__, `source `__). + .. csv-table:: :header-rows: 1 :class: fullWidthTable @@ -84,6 +96,10 @@ Go built-in support Java built-in support ================================== +Provided by the current versions of the +CodeQL query pack ``codeql/java-queries`` (`changelog `__, `source `__) +and the CodeQL library pack ``codeql/java-all`` (`changelog `__, `source `__). + .. csv-table:: :header-rows: 1 :class: fullWidthTable @@ -113,6 +129,10 @@ Java built-in support JavaScript and TypeScript built-in support ======================================================= +Provided by the current versions of the +CodeQL query pack ``codeql/javascript-queries`` (`changelog `__, `source `__) +and the CodeQL library pack ``codeql/javascript-all`` (`changelog `__, `source `__). + .. csv-table:: :header-rows: 1 :class: fullWidthTable @@ -156,6 +176,10 @@ JavaScript and TypeScript built-in support Python built-in support ==================================== +Provided by the current versions of the +CodeQL query pack ``codeql/python-queries`` (`changelog `__, `source `__) +and the CodeQL library pack ``codeql/python-all`` (`changelog `__, `source `__). + .. csv-table:: :header-rows: 1 :class: fullWidthTable From 3527897eff418d15ca8b0dfa6e9db996422c56ba Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Wed, 20 Jul 2022 09:13:34 +0200 Subject: [PATCH 300/505] Swift: make `type` optional in `TypeRepr` A type representation may not have a type in unresolved things, which for example pop up in inactive `#if` clauses. --- swift/codegen/schema.yml | 2 +- swift/extractor/visitors/TypeVisitor.cpp | 2 +- swift/ql/lib/codeql/swift/generated/type/TypeRepr.qll | 4 +++- swift/ql/lib/swift.dbscheme | 7 ++++++- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/swift/codegen/schema.yml b/swift/codegen/schema.yml index 525318cc09e..f810bfec636 100644 --- a/swift/codegen/schema.yml +++ b/swift/codegen/schema.yml @@ -184,7 +184,7 @@ Stmt: TypeRepr: _extends: AstNode - type: Type + type: Type? # type can be absent on unresolved entities FunctionType: _extends: AnyFunctionType diff --git a/swift/extractor/visitors/TypeVisitor.cpp b/swift/extractor/visitors/TypeVisitor.cpp index 7920b6084ef..ddc253c90a5 100644 --- a/swift/extractor/visitors/TypeVisitor.cpp +++ b/swift/extractor/visitors/TypeVisitor.cpp @@ -11,7 +11,7 @@ void TypeVisitor::visit(swift::TypeBase* type) { codeql::TypeRepr TypeVisitor::translateTypeRepr(const swift::TypeRepr& typeRepr, swift::Type type) { auto entry = dispatcher_.createEntry(typeRepr); - entry.type = dispatcher_.fetchLabel(type); + entry.type = dispatcher_.fetchOptionalLabel(type); return entry; } diff --git a/swift/ql/lib/codeql/swift/generated/type/TypeRepr.qll b/swift/ql/lib/codeql/swift/generated/type/TypeRepr.qll index 829960deda9..5cc1cc04147 100644 --- a/swift/ql/lib/codeql/swift/generated/type/TypeRepr.qll +++ b/swift/ql/lib/codeql/swift/generated/type/TypeRepr.qll @@ -7,8 +7,10 @@ class TypeReprBase extends @type_repr, AstNode { Type getType() { exists(Type x | - type_reprs(this, x) and + type_repr_types(this, x) and result = x.resolve() ) } + + predicate hasType() { exists(getType()) } } diff --git a/swift/ql/lib/swift.dbscheme b/swift/ql/lib/swift.dbscheme index d4f95d5122a..0826b2b9ef4 100644 --- a/swift/ql/lib/swift.dbscheme +++ b/swift/ql/lib/swift.dbscheme @@ -473,7 +473,12 @@ expr_types( //dir=expr ; type_reprs( //dir=type - unique int id: @type_repr, + unique int id: @type_repr +); + +#keyset[id] +type_repr_types( //dir=type + int id: @type_repr ref, int type_: @type ref ); From e9e5d948b33e8ba3f84f417aedaeb7cc3445b72a Mon Sep 17 00:00:00 2001 From: Cornelius Riemenschneider Date: Thu, 9 Sep 2021 17:21:08 +0000 Subject: [PATCH 301/505] C#: Implement proper `dotnet build` handling in the Lua tracing config. For proper C# tracing, `dotnet build` needs the parameter /p:UseSharedCompilation=false. However, we can't pass that to the other subcommands of `dotnet`, therefore we need to figure out which subcommand of `dotnet` is being invoked. --- csharp/tools/tracing-config.lua | 61 ++++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 8 deletions(-) diff --git a/csharp/tools/tracing-config.lua b/csharp/tools/tracing-config.lua index b4ff0206b02..8aafc63e7e5 100644 --- a/csharp/tools/tracing-config.lua +++ b/csharp/tools/tracing-config.lua @@ -2,7 +2,54 @@ function RegisterExtractorPack(id) local extractor = GetPlatformToolsDirectory() .. 'Semmle.Extraction.CSharp.Driver' if OperatingSystem == 'windows' then extractor = extractor .. '.exe' end + + function DotnetMatcherBuild(compilerName, compilerPath, compilerArguments, + _languageId) + if compilerName ~= 'dotnet' and compilerName ~= 'dotnet.exe' then + return nil + end + + -- The dotnet CLI has the following usage instructions: + -- dotnet [sdk-options] [command] [command-options] [arguments] + -- we are interested in dotnet build, which has the following usage instructions: + -- dotnet [options] build [...] + -- For now, parse the command line as follows: + -- Everything that starts with `-` (or `/`) will be ignored. + -- The first non-option argument is treated as the command. + -- if that's `build`, we append `/p:UseSharedCompilation=false` to the command line, + -- otherwise we do nothing. + local match = false + local argv = compilerArguments.argv + if OperatingSystem == 'windows' then + -- let's hope that this split matches the escaping rules `dotnet` applies to command line arguments + -- or, at least, that it is close enough + argv = + NativeArgumentsToArgv(compilerArguments.nativeArgumentPointer) + end + for i, arg in ipairs(argv) do + -- dotnet options start with either - or / (both are legal) + local firstCharacter = string.sub(arg, 1, 1) + if not (firstCharacter == '-') and not (firstCharacter == '/') then + Log(1, 'Dotnet subcommand detected: %s', arg) + if arg == 'build' then match = true end + break + end + end + if match then + return { + order = ORDER_REPLACE, + invocation = BuildExtractorInvocation(id, compilerPath, + compilerPath, + compilerArguments, nil, { + '/p:UseSharedCompilation=false' + }) + } + end + return nil + end + local windowsMatchers = { + DotnetMatcherBuild, CreatePatternMatcher({'^dotnet%.exe$'}, MatchCompilerName, extractor, { prepend = {'--dotnetexec', '--cil'}, order = ORDER_BEFORE @@ -10,22 +57,21 @@ function RegisterExtractorPack(id) CreatePatternMatcher({'^csc.*%.exe$'}, MatchCompilerName, extractor, { prepend = {'--compiler', '"${compiler}"', '--cil'}, order = ORDER_BEFORE - }), CreatePatternMatcher({'^fakes.*%.exe$', 'moles.*%.exe'}, MatchCompilerName, nil, {trace = false}) } local posixMatchers = { - CreatePatternMatcher({'^mcs%.exe$', '^csc%.exe$'}, MatchCompilerName, - extractor, { - prepend = {'--compiler', '"${compiler}"', '--cil'}, - order = ORDER_BEFORE - - }), + DotnetMatcherBuild, CreatePatternMatcher({'^mono', '^dotnet$'}, MatchCompilerName, extractor, { prepend = {'--dotnetexec', '--cil'}, order = ORDER_BEFORE + }), + CreatePatternMatcher({'^mcs%.exe$', '^csc%.exe$'}, MatchCompilerName, + extractor, { + prepend = {'--compiler', '"${compiler}"', '--cil'}, + order = ORDER_BEFORE }), function(compilerName, compilerPath, compilerArguments, _languageId) if MatchCompilerName('^msbuild$', compilerName, compilerPath, compilerArguments) or @@ -49,7 +95,6 @@ function RegisterExtractorPack(id) else return posixMatchers end - end -- Return a list of minimum supported versions of the configuration file format From 694d6395d581a4f2a876754c49046606c519f368 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Wed, 20 Jul 2022 16:25:33 +0200 Subject: [PATCH 302/505] C++: Fix join-order problem in `cpp/command-line-injection` Before on Abseil Linux: ``` Evaluated relational algebra for predicate ExecTainted::ExecState#class#91000ffb#fff@41084cm7 with tuple counts: 40879811 ~0% {2} r1 = SCAN DataFlowUtil::Node::getLocation#dispred#f0820431#ff OUTPUT In.1, In.0 40879811 ~0% {2} r2 = JOIN r1 WITH Location::Location::toString#dispred#f0820431#ff ON FIRST 1 OUTPUT Lhs.1, Rhs.1 7527 ~3% {3} r3 = JOIN r2 WITH ExecTainted::interestingConcatenation#91000ffb#ff_10#join_rhs ON FIRST 1 OUTPUT Rhs.1, Lhs.1, Lhs.0 7527 ~0% {4} r4 = JOIN r3 WITH DataFlowUtil::Node::toString#dispred#f0820431#ff ON FIRST 1 OUTPUT Lhs.2, Lhs.1, Lhs.0, Rhs.1 7527 ~0% {5} r5 = JOIN r4 WITH DataFlowUtil::Node::toString#dispred#f0820431#ff ON FIRST 1 OUTPUT Lhs.2, Lhs.1, Lhs.0, Lhs.3, Rhs.1 7527 ~0% {6} r6 = JOIN r5 WITH DataFlowUtil::Node::getLocation#dispred#f0820431#ff ON FIRST 1 OUTPUT Rhs.1, Lhs.1, Lhs.2, Lhs.0, Lhs.3, Lhs.4 7527 ~0% {3} r7 = JOIN r6 WITH Location::Location::toString#dispred#f0820431#ff ON FIRST 1 OUTPUT ((((((("ExecState (" ++ Rhs.1) ++ " | ") ++ Lhs.4) ++ ", ") ++ Lhs.1) ++ " | ") ++ Lhs.5 ++ ")"), Lhs.3, Lhs.2 return r7 ``` After: ``` Evaluated relational algebra for predicate ExecTainted::ExecState#class#91000ffb#fff@1ffe61ps with tuple counts: 7527 ~0% {3} r1 = JOIN ExecTainted::interestingConcatenation#91000ffb#ff WITH DataFlowUtil::Node::toString#dispred#f0820431#ff ON FIRST 1 OUTPUT Lhs.1, Lhs.0, Rhs.1 7527 ~0% {4} r2 = JOIN r1 WITH DataFlowUtil::Node::toString#dispred#f0820431#ff ON FIRST 1 OUTPUT Lhs.0, Lhs.1, Lhs.2, Rhs.1 7527 ~1% {5} r3 = JOIN r2 WITH DataFlowUtil::Node::getLocation#dispred#f0820431#ff ON FIRST 1 OUTPUT Rhs.1, Lhs.1, Lhs.0, Lhs.2, Lhs.3 7527 ~0% {5} r4 = JOIN r3 WITH Location::Location::toString#dispred#f0820431#ff ON FIRST 1 OUTPUT Lhs.1, Lhs.2, Lhs.3, Lhs.4, Rhs.1 7527 ~4% {6} r5 = JOIN r4 WITH DataFlowUtil::Node::getLocation#dispred#f0820431#ff ON FIRST 1 OUTPUT Rhs.1, Lhs.0, Lhs.1, Lhs.2, Lhs.3, Lhs.4 7527 ~0% {3} r6 = JOIN r5 WITH Location::Location::toString#dispred#f0820431#ff ON FIRST 1 OUTPUT ((((((("ExecState (" ++ Rhs.1) ++ " | ") ++ Lhs.3) ++ ", ") ++ Lhs.5) ++ " | ") ++ Lhs.4 ++ ")"), Lhs.1, Lhs.2 return r6 ``` --- cpp/ql/src/Security/CWE/CWE-078/ExecTainted.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.ql b/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.ql index 73fcf034096..7a3877f638c 100644 --- a/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.ql +++ b/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.ql @@ -77,7 +77,7 @@ class ExecState extends DataFlow::FlowState { ExecState() { this = "ExecState (" + fst.getLocation() + " | " + fst + ", " + snd.getLocation() + " | " + snd + ")" and - interestingConcatenation(fst, snd) + interestingConcatenation(pragma[only_bind_into](fst), pragma[only_bind_into](snd)) } DataFlow::Node getFstNode() { result = fst } From 8d80e0332efafa59f80632b233de2821e5993691 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Wed, 20 Jul 2022 16:13:46 +0200 Subject: [PATCH 303/505] Ruby: update tree-sitter-ruby --- ruby/Cargo.lock | Bin 15139 -> 15139 bytes ruby/extractor/Cargo.toml | 2 +- ruby/generator/Cargo.toml | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ruby/Cargo.lock b/ruby/Cargo.lock index 1be66824a3e4184f0dabbebc3fec55848f3da549..3d7e6eb5713079c870275af07d906cca3d12cdca 100644 GIT binary patch delta 102 zcmZ2nwzzDAw^?ASxoL`liHU(pvSmt&xnW|8nT3U^L5gXznYmGtk&#)7X{wo}Nt&@T Uu?i Date: Mon, 18 Jul 2022 12:52:17 +0100 Subject: [PATCH 304/505] Kotlin: fix for-loop iterators over primitive or wildcard types Array<*> can't be queried for an argument type, and IntArray doesn't have an argument at all; both were previously causing the extractor to fail to extract the whole file due to throwing an exception. --- .../src/main/kotlin/KotlinFileExtractor.kt | 8 +++++++- .../library-tests/for-array-iterators/test.expected | 9 +++++++++ .../kotlin/library-tests/for-array-iterators/test.kt | 11 +++++++++++ .../kotlin/library-tests/for-array-iterators/test.ql | 4 ++++ 4 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 java/ql/test/kotlin/library-tests/for-array-iterators/test.expected create mode 100644 java/ql/test/kotlin/library-tests/for-array-iterators/test.kt create mode 100644 java/ql/test/kotlin/library-tests/for-array-iterators/test.ql diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt index 9bdf40fdca0..78a81fb0715 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt @@ -2124,7 +2124,13 @@ open class KotlinFileExtractor( } isFunction(target, "kotlin", "(some array type)", { isArrayType(it) }, "iterator") && c.origin == IrStatementOrigin.FOR_LOOP_ITERATOR -> { findTopLevelFunctionOrWarn("kotlin.jvm.internal.iterator", "kotlin.jvm.internal.ArrayIteratorKt", c)?.let { iteratorFn -> - extractRawMethodAccess(iteratorFn, c, callable, parent, idx, enclosingStmt, listOf(c.dispatchReceiver), null, null, listOf((c.dispatchReceiver!!.type as IrSimpleType).arguments.first().typeOrNull!!)) + val typeArgs = (c.dispatchReceiver!!.type as IrSimpleType).arguments.map { + when(it) { + is IrTypeProjection -> it.type + else -> pluginContext.irBuiltIns.anyNType + } + } + extractRawMethodAccess(iteratorFn, c, callable, parent, idx, enclosingStmt, listOf(c.dispatchReceiver), null, null, typeArgs) } } isFunction(target, "kotlin", "(some array type)", { isArrayType(it) }, "get") && c.origin == IrStatementOrigin.GET_ARRAY_ELEMENT -> { diff --git a/java/ql/test/kotlin/library-tests/for-array-iterators/test.expected b/java/ql/test/kotlin/library-tests/for-array-iterators/test.expected new file mode 100644 index 00000000000..ac5821044ff --- /dev/null +++ b/java/ql/test/kotlin/library-tests/for-array-iterators/test.expected @@ -0,0 +1,9 @@ +| test.kt:5:14:5:14 | hasNext(...) | +| test.kt:5:14:5:14 | iterator(...) | +| test.kt:5:14:5:14 | next(...) | +| test.kt:6:14:6:14 | hasNext(...) | +| test.kt:6:14:6:14 | iterator(...) | +| test.kt:6:14:6:14 | next(...) | +| test.kt:7:14:7:14 | hasNext(...) | +| test.kt:7:14:7:14 | iterator(...) | +| test.kt:7:14:7:14 | next(...) | diff --git a/java/ql/test/kotlin/library-tests/for-array-iterators/test.kt b/java/ql/test/kotlin/library-tests/for-array-iterators/test.kt new file mode 100644 index 00000000000..2da3a6e1e0e --- /dev/null +++ b/java/ql/test/kotlin/library-tests/for-array-iterators/test.kt @@ -0,0 +1,11 @@ +fun test(x: Array, y: Array<*>, z: IntArray): Int { + + var ret = 0 + + for (el in x) { ret += 1 } + for (el in y) { ret += 1 } + for (el in z) { ret += 1 } + + return ret + +} diff --git a/java/ql/test/kotlin/library-tests/for-array-iterators/test.ql b/java/ql/test/kotlin/library-tests/for-array-iterators/test.ql new file mode 100644 index 00000000000..ab60ba2525d --- /dev/null +++ b/java/ql/test/kotlin/library-tests/for-array-iterators/test.ql @@ -0,0 +1,4 @@ +import java + +from MethodAccess ma +select ma From 9593ceeda54855bf6e6edd830710542e84a54306 Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Tue, 19 Jul 2022 14:48:00 +0100 Subject: [PATCH 305/505] Kotlin: Special-case String.charAt naming In the Kotlin universe this is called `get` so that Kotlin programmers can use the `[]` operator on `String`s. --- java/kotlin-extractor/src/main/kotlin/utils/JvmNames.kt | 2 ++ java/ql/test/kotlin/library-tests/string-charat/Test.java | 5 +++++ .../ql/test/kotlin/library-tests/string-charat/test.expected | 2 ++ java/ql/test/kotlin/library-tests/string-charat/test.kt | 2 ++ java/ql/test/kotlin/library-tests/string-charat/test.ql | 4 ++++ 5 files changed, 15 insertions(+) create mode 100644 java/ql/test/kotlin/library-tests/string-charat/Test.java create mode 100644 java/ql/test/kotlin/library-tests/string-charat/test.expected create mode 100644 java/ql/test/kotlin/library-tests/string-charat/test.kt create mode 100644 java/ql/test/kotlin/library-tests/string-charat/test.ql diff --git a/java/kotlin-extractor/src/main/kotlin/utils/JvmNames.kt b/java/kotlin-extractor/src/main/kotlin/utils/JvmNames.kt index 57a3e92e6b2..76d155f8b22 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/JvmNames.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/JvmNames.kt @@ -51,6 +51,8 @@ private val specialFunctions = mapOf( makeDescription(FqName("java.lang.Number"), "toFloat") to "floatValue", makeDescription(StandardNames.FqNames.number.toSafe(), "toDouble") to "doubleValue", makeDescription(FqName("java.lang.Number"), "toDouble") to "doubleValue", + makeDescription(StandardNames.FqNames.string.toSafe(), "get") to "charAt", + makeDescription(FqName("java.lang.String"), "get") to "charAt", ) private val specialFunctionShortNames = specialFunctions.keys.map { it.functionName }.toSet() diff --git a/java/ql/test/kotlin/library-tests/string-charat/Test.java b/java/ql/test/kotlin/library-tests/string-charat/Test.java new file mode 100644 index 00000000000..22f553216b7 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/string-charat/Test.java @@ -0,0 +1,5 @@ +public class Test { + + public char f(String s) { return s.charAt(0); } + +} diff --git a/java/ql/test/kotlin/library-tests/string-charat/test.expected b/java/ql/test/kotlin/library-tests/string-charat/test.expected new file mode 100644 index 00000000000..ab1ff9b6d5f --- /dev/null +++ b/java/ql/test/kotlin/library-tests/string-charat/test.expected @@ -0,0 +1,2 @@ +| Test.java:3:36:3:46 | charAt(...) | +| test.kt:2:20:2:23 | charAt(...) | diff --git a/java/ql/test/kotlin/library-tests/string-charat/test.kt b/java/ql/test/kotlin/library-tests/string-charat/test.kt new file mode 100644 index 00000000000..c586f3d0171 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/string-charat/test.kt @@ -0,0 +1,2 @@ + +fun f(x: String) = x[0] diff --git a/java/ql/test/kotlin/library-tests/string-charat/test.ql b/java/ql/test/kotlin/library-tests/string-charat/test.ql new file mode 100644 index 00000000000..ab60ba2525d --- /dev/null +++ b/java/ql/test/kotlin/library-tests/string-charat/test.ql @@ -0,0 +1,4 @@ +import java + +from MethodAccess ma +select ma From ad8335d6f3b498c8a70f4ffdaaae214d018acb7e Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Wed, 20 Jul 2022 20:12:39 +0200 Subject: [PATCH 306/505] C++: Fix join-order problem in `cpp/return-stack-allocated-memory` Before on Abseil: ``` Evaluated relational algebra for predicate #select#cpe#12356#fffff@3ffb21o1 with tuple counts: 1235939 ~0% {2} r1 = SCAN functions OUTPUT In.0, In.0 1235939 ~0% {2} r2 = JOIN r1 WITH functions ON FIRST 1 OUTPUT Lhs.1, Lhs.0 33500841 ~0% {2} r3 = JOIN r2 WITH DataFlowUtil::Node::getEnclosingCallable#dispred#f0820431#ff_10#join_rhs ON FIRST 1 OUTPUT Rhs.1, Lhs.1 280683 ~3% {3} r4 = JOIN r3 WITH MustFlow::MkLocalPathNode#0227f5a1#fff ON FIRST 1 OUTPUT Rhs.2, Lhs.1, Lhs.0 40970 ~2% {4} r5 = JOIN r4 WITH MustFlow::MustFlowConfiguration::hasFlowPath#dispred#f0820431#fff#cpe#23_10#join_rhs ON FIRST 1 OUTPUT Rhs.1, Lhs.1, Lhs.2, Lhs.0 40970 ~0% {5} r6 = JOIN r5 WITH MustFlow::MkLocalPathNode#0227f5a1#fff_20#join_rhs ON FIRST 1 OUTPUT Rhs.1, Lhs.1, Lhs.2, Lhs.3, Lhs.0 40970 ~1% {5} r7 = JOIN r6 WITH DataFlowUtil::Cached::TInstructionNode#47741e1f#ff_10#join_rhs ON FIRST 1 OUTPUT Rhs.1, Lhs.1, Lhs.2, Lhs.3, Lhs.4 40970 ~1% {5} r8 = JOIN r7 WITH project#Instruction::VariableAddressInstruction#class#577b6a83#ff ON FIRST 1 OUTPUT Lhs.0, Lhs.1, Lhs.2, Lhs.3, Lhs.4 40970 ~0% {6} r9 = JOIN r8 WITH SSAConstruction::Cached::getInstructionAst#2b11997e#ff ON FIRST 1 OUTPUT Lhs.0, Lhs.1, Lhs.2, Lhs.3, Lhs.4, Rhs.1 40970 ~2% {7} r10 = JOIN r9 WITH SSAConstruction::Cached::getInstructionAst#2b11997e#ff ON FIRST 1 OUTPUT Lhs.0, Lhs.1, Lhs.2, Lhs.3, Lhs.4, Lhs.5, Rhs.1 0 ~0% {6} r11 = JOIN r10 WITH Instruction::Instruction::getEnclosingFunction#dispred#f0820431#3#ff ON FIRST 2 OUTPUT Rhs.1, Lhs.2, Lhs.3, Lhs.4, Lhs.5, Lhs.6 0 ~0% {5} r12 = JOIN r11 WITH functions ON FIRST 1 OUTPUT Lhs.5, Lhs.1, Lhs.2, Lhs.3, Lhs.4 0 ~0% {5} r13 = JOIN r12 WITH Element::ElementBase::toString#dispred#f0820431#ff ON FIRST 1 OUTPUT Lhs.1, Lhs.3, Lhs.2, Lhs.4, Rhs.1 return r13 ``` After: ``` Evaluated relational algebra for predicate #select#cpe#12356#fffff@1dbc97kv with tuple counts: 40970 ~0% {2} r1 = SCAN MustFlow::MustFlowConfiguration::hasFlowPath#dispred#f0820431#fff#cpe#23 OUTPUT In.1, In.0 40970 ~0% {3} r2 = JOIN r1 WITH MustFlow::MkLocalPathNode#0227f5a1#fff_20#join_rhs ON FIRST 1 OUTPUT Lhs.1, Lhs.0, Rhs.1 40970 ~7% {4} r3 = JOIN r2 WITH MustFlow::MkLocalPathNode#0227f5a1#fff_20#join_rhs ON FIRST 1 OUTPUT Rhs.1, Lhs.0, Lhs.1, Lhs.2 40970 ~2% {4} r4 = JOIN r3 WITH DataFlowUtil::Cached::TInstructionNode#47741e1f#ff_10#join_rhs ON FIRST 1 OUTPUT Rhs.1, Lhs.1, Lhs.2, Lhs.3 40970 ~2% {4} r5 = JOIN r4 WITH project#Instruction::VariableAddressInstruction#class#577b6a83#ff ON FIRST 1 OUTPUT Lhs.0, Lhs.1, Lhs.2, Lhs.3 40970 ~0% {5} r6 = JOIN r5 WITH SSAConstruction::Cached::getInstructionAst#2b11997e#ff ON FIRST 1 OUTPUT Lhs.0, Lhs.1, Lhs.2, Lhs.3, Rhs.1 40970 ~1% {6} r7 = JOIN r6 WITH SSAConstruction::Cached::getInstructionAst#2b11997e#ff ON FIRST 1 OUTPUT Lhs.0, Lhs.1, Lhs.2, Lhs.3, Lhs.4, Rhs.1 40970 ~0% {6} r8 = JOIN r7 WITH Instruction::Instruction::getEnclosingFunction#dispred#f0820431#3#ff ON FIRST 1 OUTPUT Lhs.3, Rhs.1, Lhs.1, Lhs.2, Lhs.4, Lhs.5 0 ~0% {5} r9 = JOIN r8 WITH DataFlowUtil::Node::getEnclosingCallable#dispred#f0820431#fb ON FIRST 2 OUTPUT Lhs.5, Lhs.2, Lhs.3, Lhs.0, Lhs.4 0 ~0% {5} r10 = JOIN r9 WITH Element::ElementBase::toString#dispred#f0820431#ff ON FIRST 1 OUTPUT Lhs.3, Lhs.1, Lhs.2, Lhs.4, Rhs.1 return r10 ``` --- .../Memory Management/ReturnStackAllocatedMemory.ql | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql b/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql index bd55008677c..7eab1bd03c8 100644 --- a/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql +++ b/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql @@ -74,13 +74,12 @@ class ReturnStackAllocatedMemoryConfig extends MustFlowConfiguration { from MustFlowPathNode source, MustFlowPathNode sink, VariableAddressInstruction var, - ReturnStackAllocatedMemoryConfig conf, Function f + ReturnStackAllocatedMemoryConfig conf where - conf.hasFlowPath(source, sink) and + conf.hasFlowPath(pragma[only_bind_into](source), pragma[only_bind_into](sink)) and source.getNode().asInstruction() = var and // Only raise an alert if we're returning from the _same_ callable as the on that // declared the stack variable. - var.getEnclosingFunction() = pragma[only_bind_into](f) and - sink.getNode().getEnclosingCallable() = pragma[only_bind_into](f) + var.getEnclosingFunction() = sink.getNode().getEnclosingCallable() select sink.getNode(), source, sink, "May return stack-allocated memory from $@.", var.getAst(), var.getAst().toString() From 388c9ffb745c833929dfcf53fd38c3e464d5ccaf Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Mon, 1 Nov 2021 16:24:28 +0000 Subject: [PATCH 307/505] Ruby: separate trap-writer into its own module --- ruby/extractor/src/extractor.rs | 454 +++++++++----------------------- ruby/extractor/src/main.rs | 66 +---- ruby/extractor/src/trap.rs | 272 +++++++++++++++++++ 3 files changed, 410 insertions(+), 382 deletions(-) create mode 100644 ruby/extractor/src/trap.rs diff --git a/ruby/extractor/src/extractor.rs b/ruby/extractor/src/extractor.rs index 8cdaff1b738..6c462e11bb4 100644 --- a/ruby/extractor/src/extractor.rs +++ b/ruby/extractor/src/extractor.rs @@ -1,161 +1,112 @@ +use crate::trap; use node_types::{EntryKind, Field, NodeTypeMap, Storage, TypeName}; -use std::borrow::Cow; use std::collections::BTreeMap as Map; use std::collections::BTreeSet as Set; use std::fmt; -use std::io::Write; use std::path::Path; use tracing::{error, info, span, Level}; use tree_sitter::{Language, Node, Parser, Range, Tree}; -pub struct TrapWriter { - /// The accumulated trap entries - trap_output: Vec, - /// A counter for generating fresh labels - counter: u32, - /// cache of global keys - global_keys: std::collections::HashMap, +pub fn populate_file(writer: &mut trap::Writer, absolute_path: &Path) -> trap::Label { + let (file_label, fresh) = + writer.global_id(&trap::full_id_for_file(&normalize_path(absolute_path))); + if fresh { + writer.add_tuple( + "files", + vec![ + trap::Arg::Label(file_label), + trap::Arg::String(normalize_path(absolute_path)), + ], + ); + populate_parent_folders(writer, file_label, absolute_path.parent()); + } + file_label } -pub fn new_trap_writer() -> TrapWriter { - TrapWriter { - counter: 0, - trap_output: Vec::new(), - global_keys: std::collections::HashMap::new(), +fn populate_empty_file(writer: &mut trap::Writer) -> trap::Label { + let (file_label, fresh) = writer.global_id("empty;sourcefile"); + if fresh { + writer.add_tuple( + "files", + vec![ + trap::Arg::Label(file_label), + trap::Arg::String("".to_string()), + ], + ); } + file_label } -impl TrapWriter { - /// Gets a label that will hold the unique ID of the passed string at import time. - /// This can be used for incrementally importable TRAP files -- use globally unique - /// strings to compute a unique ID for table tuples. - /// - /// Note: You probably want to make sure that the key strings that you use are disjoint - /// for disjoint column types; the standard way of doing this is to prefix (or append) - /// the column type name to the ID. Thus, you might identify methods in Java by the - /// full ID "methods_com.method.package.DeclaringClass.method(argumentList)". +pub fn populate_empty_location(writer: &mut trap::Writer) { + let file_label = populate_empty_file(writer); + location(writer, file_label, 0, 0, 0, 0); +} - fn fresh_id(&mut self) -> Label { - let label = Label(self.counter); - self.counter += 1; - self.trap_output.push(TrapEntry::FreshId(label)); - label - } - - fn global_id(&mut self, key: &str) -> (Label, bool) { - if let Some(label) = self.global_keys.get(key) { - return (*label, false); - } - let label = Label(self.counter); - self.counter += 1; - self.global_keys.insert(key.to_owned(), label); - self.trap_output - .push(TrapEntry::MapLabelToKey(label, key.to_owned())); - (label, true) - } - - fn add_tuple(&mut self, table_name: &str, args: Vec) { - self.trap_output - .push(TrapEntry::GenericTuple(table_name.to_owned(), args)) - } - - fn populate_file(&mut self, absolute_path: &Path) -> Label { - let (file_label, fresh) = self.global_id(&full_id_for_file(absolute_path)); - if fresh { - self.add_tuple( - "files", - vec![ - Arg::Label(file_label), - Arg::String(normalize_path(absolute_path)), - ], - ); - self.populate_parent_folders(file_label, absolute_path.parent()); - } - file_label - } - - fn populate_empty_file(&mut self) -> Label { - let (file_label, fresh) = self.global_id("empty;sourcefile"); - if fresh { - self.add_tuple( - "files", - vec![Arg::Label(file_label), Arg::String("".to_string())], - ); - } - file_label - } - - pub fn populate_empty_location(&mut self) { - let file_label = self.populate_empty_file(); - self.location(file_label, 0, 0, 0, 0); - } - - fn populate_parent_folders(&mut self, child_label: Label, path: Option<&Path>) { - let mut path = path; - let mut child_label = child_label; - loop { - match path { - None => break, - Some(folder) => { - let (folder_label, fresh) = self.global_id(&full_id_for_folder(folder)); - self.add_tuple( - "containerparent", - vec![Arg::Label(folder_label), Arg::Label(child_label)], +pub fn populate_parent_folders( + writer: &mut trap::Writer, + child_label: trap::Label, + path: Option<&Path>, +) { + let mut path = path; + let mut child_label = child_label; + loop { + match path { + None => break, + Some(folder) => { + let (folder_label, fresh) = + writer.global_id(&trap::full_id_for_folder(&normalize_path(folder))); + writer.add_tuple( + "containerparent", + vec![ + trap::Arg::Label(folder_label), + trap::Arg::Label(child_label), + ], + ); + if fresh { + writer.add_tuple( + "folders", + vec![ + trap::Arg::Label(folder_label), + trap::Arg::String(normalize_path(folder)), + ], ); - if fresh { - self.add_tuple( - "folders", - vec![ - Arg::Label(folder_label), - Arg::String(normalize_path(folder)), - ], - ); - path = folder.parent(); - child_label = folder_label; - } else { - break; - } + path = folder.parent(); + child_label = folder_label; + } else { + break; } } } } +} - fn location( - &mut self, - file_label: Label, - start_line: usize, - start_column: usize, - end_line: usize, - end_column: usize, - ) -> Label { - let (loc_label, fresh) = self.global_id(&format!( - "loc,{{{}}},{},{},{},{}", - file_label, start_line, start_column, end_line, end_column - )); - if fresh { - self.add_tuple( - "locations_default", - vec![ - Arg::Label(loc_label), - Arg::Label(file_label), - Arg::Int(start_line), - Arg::Int(start_column), - Arg::Int(end_line), - Arg::Int(end_column), - ], - ); - } - loc_label - } - - fn comment(&mut self, text: String) { - self.trap_output.push(TrapEntry::Comment(text)); - } - - pub fn output(self, writer: &mut dyn Write) -> std::io::Result<()> { - write!(writer, "{}", Program(self.trap_output)) +fn location( + writer: &mut trap::Writer, + file_label: trap::Label, + start_line: usize, + start_column: usize, + end_line: usize, + end_column: usize, +) -> trap::Label { + let (loc_label, fresh) = writer.global_id(&format!( + "loc,{{{}}},{},{},{},{}", + file_label, start_line, start_column, end_line, end_column + )); + if fresh { + writer.add_tuple( + "locations_default", + vec![ + trap::Arg::Label(loc_label), + trap::Arg::Label(file_label), + trap::Arg::Int(start_line), + trap::Arg::Int(start_column), + trap::Arg::Int(end_line), + trap::Arg::Int(end_column), + ], + ); } + loc_label } /// Extracts the source file at `path`, which is assumed to be canonicalized. @@ -163,7 +114,7 @@ pub fn extract( language: Language, language_prefix: &str, schema: &NodeTypeMap, - trap_writer: &mut TrapWriter, + trap_writer: &mut trap::Writer, path: &Path, source: &[u8], ranges: &[Range], @@ -183,13 +134,13 @@ pub fn extract( parser.set_included_ranges(ranges).unwrap(); let tree = parser.parse(&source, None).expect("Failed to parse file"); trap_writer.comment(format!("Auto-generated TRAP file for {}", path.display())); - let file_label = &trap_writer.populate_file(path); + let file_label = populate_file(trap_writer, path); let mut visitor = Visitor { source, trap_writer, // TODO: should we handle path strings that are not valid UTF8 better? path: format!("{}", path.display()), - file_label: *file_label, + file_label, toplevel_child_counter: 0, stack: Vec::new(), language_prefix, @@ -201,33 +152,6 @@ pub fn extract( Ok(()) } -/// Escapes a string for use in a TRAP key, by replacing special characters with -/// HTML entities. -fn escape_key<'a, S: Into>>(key: S) -> Cow<'a, str> { - fn needs_escaping(c: char) -> bool { - matches!(c, '&' | '{' | '}' | '"' | '@' | '#') - } - - let key = key.into(); - if key.contains(needs_escaping) { - let mut escaped = String::with_capacity(2 * key.len()); - for c in key.chars() { - match c { - '&' => escaped.push_str("&"), - '{' => escaped.push_str("{"), - '}' => escaped.push_str("}"), - '"' => escaped.push_str("""), - '@' => escaped.push_str("@"), - '#' => escaped.push_str("#"), - _ => escaped.push(c), - } - } - Cow::Owned(escaped) - } else { - key - } -} - /// Normalizes the path according the common CodeQL specification. Assumes that /// `path` has already been canonicalized using `std::fs::canonicalize`. fn normalize_path(path: &Path) -> String { @@ -267,17 +191,9 @@ fn normalize_path(path: &Path) -> String { } } -fn full_id_for_file(path: &Path) -> String { - format!("{};sourcefile", escape_key(&normalize_path(path))) -} - -fn full_id_for_folder(path: &Path) -> String { - format!("{};folder", escape_key(&normalize_path(path))) -} - struct ChildNode { field_name: Option<&'static str>, - label: Label, + label: trap::Label, type_name: TypeName, } @@ -286,11 +202,11 @@ struct Visitor<'a> { path: String, /// The label to use whenever we need to refer to the `@file` entity of this /// source file. - file_label: Label, + file_label: trap::Label, /// The source code as a UTF-8 byte array source: &'a [u8], - /// A TrapWriter to accumulate trap entries - trap_writer: &'a mut TrapWriter, + /// A trap::Writer to accumulate trap entries + trap_writer: &'a mut trap::Writer, /// A counter for top-level child nodes toplevel_child_counter: usize, /// Language prefix @@ -303,7 +219,7 @@ struct Visitor<'a> { /// node the list containing the child data is popped from the stack and /// matched against the dbscheme for the node. If the expectations are met /// the corresponding row definitions are added to the trap_output. - stack: Vec<(Label, usize, Vec)>, + stack: Vec<(trap::Label, usize, Vec)>, } impl Visitor<'_> { @@ -311,19 +227,19 @@ impl Visitor<'_> { &mut self, error_message: String, full_error_message: String, - loc: Label, + loc: trap::Label, ) { error!("{}", full_error_message); let id = self.trap_writer.fresh_id(); self.trap_writer.add_tuple( "diagnostics", vec![ - Arg::Label(id), - Arg::Int(40), // severity 40 = error - Arg::String("parse_error".to_string()), - Arg::String(error_message), - Arg::String(full_error_message), - Arg::Label(loc), + trap::Arg::Label(id), + trap::Arg::Int(40), // severity 40 = error + trap::Arg::String("parse_error".to_string()), + trap::Arg::String(error_message), + trap::Arg::String(full_error_message), + trap::Arg::Label(loc), ], ); } @@ -335,7 +251,8 @@ impl Visitor<'_> { node: Node, ) { let (start_line, start_column, end_line, end_column) = location_for(self.source, node); - let loc = self.trap_writer.location( + let loc = location( + self.trap_writer, self.file_label, start_line, start_column, @@ -374,7 +291,8 @@ impl Visitor<'_> { } let (id, _, child_nodes) = self.stack.pop().expect("Vistor: empty stack"); let (start_line, start_column, end_line, end_column) = location_for(self.source, node); - let loc = self.trap_writer.location( + let loc = location( + self.trap_writer, self.file_label, start_line, start_column, @@ -404,18 +322,19 @@ impl Visitor<'_> { self.trap_writer.add_tuple( &format!("{}_ast_node_info", self.language_prefix), vec![ - Arg::Label(id), - Arg::Label(parent_id), - Arg::Int(parent_index), - Arg::Label(loc), + trap::Arg::Label(id), + trap::Arg::Label(parent_id), + trap::Arg::Int(parent_index), + trap::Arg::Label(loc), ], ); self.trap_writer.add_tuple( &format!("{}_tokeninfo", self.language_prefix), vec![ - Arg::Label(id), - Arg::Int(*kind_id), + trap::Arg::Label(id), + trap::Arg::Int(*kind_id), sliced_source_arg(self.source, node), + trap::Arg::Label(loc), ], ); } @@ -427,13 +346,13 @@ impl Visitor<'_> { self.trap_writer.add_tuple( &format!("{}_ast_node_info", self.language_prefix), vec![ - Arg::Label(id), - Arg::Label(parent_id), - Arg::Int(parent_index), - Arg::Label(loc), + trap::Arg::Label(id), + trap::Arg::Label(parent_id), + trap::Arg::Int(parent_index), + trap::Arg::Label(loc), ], ); - let mut all_args = vec![Arg::Label(id)]; + let mut all_args = vec![trap::Arg::Label(id)]; all_args.extend(args); self.trap_writer.add_tuple(table_name, all_args); } @@ -472,9 +391,9 @@ impl Visitor<'_> { node: &Node, fields: &[Field], child_nodes: &[ChildNode], - parent_id: Label, - ) -> Option> { - let mut map: Map<&Option, (&Field, Vec)> = Map::new(); + parent_id: trap::Label, + ) -> Option> { + let mut map: Map<&Option, (&Field, Vec)> = Map::new(); for field in fields { map.insert(&field.name, (field, Vec::new())); } @@ -488,9 +407,9 @@ impl Visitor<'_> { { // We can safely unwrap because type_matches checks the key is in the map. let (int_value, _) = int_mapping.get(&child_node.type_name.kind).unwrap(); - values.push(Arg::Int(*int_value)); + values.push(trap::Arg::Int(*int_value)); } else { - values.push(Arg::Label(child_node.label)); + values.push(trap::Arg::Label(child_node.label)); } } else if field.name.is_some() { let error_message = format!( @@ -569,9 +488,9 @@ impl Visitor<'_> { ); break; } - let mut args = vec![Arg::Label(parent_id)]; + let mut args = vec![trap::Arg::Label(parent_id)]; if *has_index { - args.push(Arg::Int(index)) + args.push(trap::Arg::Int(index)) } args.push(child_value.clone()); self.trap_writer.add_tuple(table_name, args); @@ -625,9 +544,9 @@ impl Visitor<'_> { } // Emit a slice of a source file as an Arg. -fn sliced_source_arg(source: &[u8], n: Node) -> Arg { +fn sliced_source_arg(source: &[u8], n: Node) -> trap::Arg { let range = n.byte_range(); - Arg::String(String::from_utf8_lossy(&source[range.start..range.end]).into_owned()) + trap::Arg::String(String::from_utf8_lossy(&source[range.start..range.end]).into_owned()) } // Emit a pair of `TrapEntry`s for the provided node, appropriately calibrated. @@ -699,59 +618,6 @@ fn traverse(tree: &Tree, visitor: &mut Visitor) { } } -pub struct Program(Vec); - -impl fmt::Display for Program { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut text = String::new(); - for trap_entry in &self.0 { - text.push_str(&format!("{}\n", trap_entry)); - } - write!(f, "{}", text) - } -} - -enum TrapEntry { - /// Maps the label to a fresh id, e.g. `#123=*`. - FreshId(Label), - /// Maps the label to a key, e.g. `#7=@"foo"`. - MapLabelToKey(Label, String), - /// foo_bar(arg*) - GenericTuple(String, Vec), - Comment(String), -} -impl fmt::Display for TrapEntry { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - TrapEntry::FreshId(label) => write!(f, "{}=*", label), - TrapEntry::MapLabelToKey(label, key) => { - write!(f, "{}=@\"{}\"", label, key.replace("\"", "\"\"")) - } - TrapEntry::GenericTuple(name, args) => { - write!(f, "{}(", name)?; - for (index, arg) in args.iter().enumerate() { - if index > 0 { - write!(f, ",")?; - } - write!(f, "{}", arg)?; - } - write!(f, ")") - } - TrapEntry::Comment(line) => write!(f, "// {}", line), - } - } -} - -#[derive(Debug, Copy, Clone)] -// Identifiers of the form #0, #1... -struct Label(u32); - -impl fmt::Display for Label { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "#{:x}", self.0) - } -} - // Numeric indices. #[derive(Debug, Copy, Clone)] struct Index(usize); @@ -761,69 +627,3 @@ impl fmt::Display for Index { write!(f, "{}", self.0) } } - -// Some untyped argument to a TrapEntry. -#[derive(Debug, Clone)] -enum Arg { - Label(Label), - Int(usize), - String(String), -} - -const MAX_STRLEN: usize = 1048576; - -impl fmt::Display for Arg { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Arg::Label(x) => write!(f, "{}", x), - Arg::Int(x) => write!(f, "{}", x), - Arg::String(x) => write!( - f, - "\"{}\"", - limit_string(x, MAX_STRLEN).replace("\"", "\"\"") - ), - } - } -} - -/// Limit the length (in bytes) of a string. If the string's length in bytes is -/// less than or equal to the limit then the entire string is returned. Otherwise -/// the string is sliced at the provided limit. If there is a multi-byte character -/// at the limit then the returned slice will be slightly shorter than the limit to -/// avoid splitting that multi-byte character. -fn limit_string(string: &str, max_size: usize) -> &str { - if string.len() <= max_size { - return string; - } - let p = string.as_bytes(); - let mut index = max_size; - // We want to clip the string at [max_size]; however, the character at that position - // may span several bytes. We need to find the first byte of the character. In UTF-8 - // encoded data any byte that matches the bit pattern 10XXXXXX is not a start byte. - // Therefore we decrement the index as long as there are bytes matching this pattern. - // This ensures we cut the string at the border between one character and another. - while index > 0 && (p[index] & 0b11000000) == 0b10000000 { - index -= 1; - } - &string[0..index] -} - -#[test] -fn limit_string_test() { - assert_eq!("hello", limit_string(&"hello world".to_owned(), 5)); - assert_eq!("hi ☹", limit_string(&"hi ☹☹".to_owned(), 6)); - assert_eq!("hi ", limit_string(&"hi ☹☹".to_owned(), 5)); -} - -#[test] -fn escape_key_test() { - assert_eq!("foo!", escape_key("foo!")); - assert_eq!("foo{}", escape_key("foo{}")); - assert_eq!("{}", escape_key("{}")); - assert_eq!("", escape_key("")); - assert_eq!("/path/to/foo.rb", escape_key("/path/to/foo.rb")); - assert_eq!( - "/path/to/foo&{}"@#.rb", - escape_key("/path/to/foo&{}\"@#.rb") - ); -} diff --git a/ruby/extractor/src/main.rs b/ruby/extractor/src/main.rs index 7e4aa973518..4fc886a50ad 100644 --- a/ruby/extractor/src/main.rs +++ b/ruby/extractor/src/main.rs @@ -1,51 +1,15 @@ mod extractor; +mod trap; extern crate num_cpus; use clap::arg; -use flate2::write::GzEncoder; use rayon::prelude::*; use std::fs; -use std::io::{BufRead, BufWriter}; +use std::io::BufRead; use std::path::{Path, PathBuf}; use tree_sitter::{Language, Parser, Range}; -enum TrapCompression { - None, - Gzip, -} - -impl TrapCompression { - fn from_env() -> TrapCompression { - match std::env::var("CODEQL_RUBY_TRAP_COMPRESSION") { - Ok(method) => match TrapCompression::from_string(&method) { - Some(c) => c, - None => { - tracing::error!("Unknown compression method '{}'; using gzip.", &method); - TrapCompression::Gzip - } - }, - // Default compression method if the env var isn't set: - Err(_) => TrapCompression::Gzip, - } - } - - fn from_string(s: &str) -> Option { - match s.to_lowercase().as_ref() { - "none" => Some(TrapCompression::None), - "gzip" => Some(TrapCompression::Gzip), - _ => None, - } - } - - fn extension(&self) -> &str { - match self { - TrapCompression::None => "trap", - TrapCompression::Gzip => "trap.gz", - } - } -} - /** * Gets the number of threads the extractor should use, by reading the * CODEQL_THREADS environment variable and using it as described in the @@ -118,7 +82,7 @@ fn main() -> std::io::Result<()> { .value_of("output-dir") .expect("missing --output-dir"); let trap_dir = PathBuf::from(trap_dir); - let trap_compression = TrapCompression::from_env(); + let trap_compression = trap::Compression::from_env("CODEQL_RUBY_TRAP_COMPRESSION"); let file_list = matches.value_of("file-list").expect("missing --file-list"); let file_list = fs::File::open(file_list)?; @@ -141,7 +105,7 @@ fn main() -> std::io::Result<()> { let src_archive_file = path_for(&src_archive_dir, &path, ""); let mut source = std::fs::read(&path)?; let code_ranges; - let mut trap_writer = extractor::new_trap_writer(); + let mut trap_writer = trap::Writer::new(); if path.extension().map_or(false, |x| x == "erb") { tracing::info!("scanning: {}", path.display()); extractor::extract( @@ -181,33 +145,25 @@ fn main() -> std::io::Result<()> { )?; std::fs::create_dir_all(&src_archive_file.parent().unwrap())?; std::fs::copy(&path, &src_archive_file)?; - write_trap(&trap_dir, path, trap_writer, &trap_compression) + write_trap(&trap_dir, path, &trap_writer, trap_compression) }) .expect("failed to extract files"); let path = PathBuf::from("extras"); - let mut trap_writer = extractor::new_trap_writer(); - trap_writer.populate_empty_location(); - write_trap(&trap_dir, path, trap_writer, &trap_compression) + let mut trap_writer = trap::Writer::new(); + extractor::populate_empty_location(&mut trap_writer); + write_trap(&trap_dir, path, &trap_writer, trap_compression) } fn write_trap( trap_dir: &Path, path: PathBuf, - trap_writer: extractor::TrapWriter, - trap_compression: &TrapCompression, + trap_writer: &trap::Writer, + trap_compression: trap::Compression, ) -> std::io::Result<()> { let trap_file = path_for(trap_dir, &path, trap_compression.extension()); std::fs::create_dir_all(&trap_file.parent().unwrap())?; - let trap_file = std::fs::File::create(&trap_file)?; - let mut trap_file = BufWriter::new(trap_file); - match trap_compression { - TrapCompression::None => trap_writer.output(&mut trap_file), - TrapCompression::Gzip => { - let mut compressed_writer = GzEncoder::new(trap_file, flate2::Compression::fast()); - trap_writer.output(&mut compressed_writer) - } - } + trap_writer.write_to_file(&trap_file, trap_compression) } fn scan_erb( diff --git a/ruby/extractor/src/trap.rs b/ruby/extractor/src/trap.rs new file mode 100644 index 00000000000..d64c520c4cc --- /dev/null +++ b/ruby/extractor/src/trap.rs @@ -0,0 +1,272 @@ +use std::borrow::Cow; +use std::fmt; +use std::io::{BufWriter, Write}; +use std::path::Path; + +use flate2::write::GzEncoder; + +pub struct Writer { + /// The accumulated trap entries + trap_output: Vec, + /// A counter for generating fresh labels + counter: u32, + /// cache of global keys + global_keys: std::collections::HashMap, +} + +impl Writer { + pub fn new() -> Writer { + Writer { + counter: 0, + trap_output: Vec::new(), + global_keys: std::collections::HashMap::new(), + } + } + + pub fn fresh_id(&mut self) -> Label { + let label = Label(self.counter); + self.counter += 1; + self.trap_output.push(Entry::FreshId(label)); + label + } + + /// Gets a label that will hold the unique ID of the passed string at import time. + /// This can be used for incrementally importable TRAP files -- use globally unique + /// strings to compute a unique ID for table tuples. + /// + /// Note: You probably want to make sure that the key strings that you use are disjoint + /// for disjoint column types; the standard way of doing this is to prefix (or append) + /// the column type name to the ID. Thus, you might identify methods in Java by the + /// full ID "methods_com.method.package.DeclaringClass.method(argumentList)". + pub fn global_id(&mut self, key: &str) -> (Label, bool) { + if let Some(label) = self.global_keys.get(key) { + return (*label, false); + } + let label = Label(self.counter); + self.counter += 1; + self.global_keys.insert(key.to_owned(), label); + self.trap_output + .push(Entry::MapLabelToKey(label, key.to_owned())); + (label, true) + } + + pub fn add_tuple(&mut self, table_name: &str, args: Vec) { + self.trap_output + .push(Entry::GenericTuple(table_name.to_owned(), args)) + } + + pub fn comment(&mut self, text: String) { + self.trap_output.push(Entry::Comment(text)); + } + + pub fn write_to_file(&self, path: &Path, compression: Compression) -> std::io::Result<()> { + let trap_file = std::fs::File::create(path)?; + let mut trap_file = BufWriter::new(trap_file); + match compression { + Compression::None => self.write_trap_entries(&mut trap_file), + Compression::Gzip => { + let mut compressed_writer = GzEncoder::new(trap_file, flate2::Compression::fast()); + self.write_trap_entries(&mut compressed_writer) + } + } + } + + fn write_trap_entries(&self, file: &mut W) -> std::io::Result<()> { + for trap_entry in &self.trap_output { + writeln!(file, "{}", trap_entry)?; + } + std::io::Result::Ok(()) + } +} + +pub enum Entry { + /// Maps the label to a fresh id, e.g. `#123=*`. + FreshId(Label), + /// Maps the label to a key, e.g. `#7=@"foo"`. + MapLabelToKey(Label, String), + /// foo_bar(arg*) + GenericTuple(String, Vec), + Comment(String), +} + +impl fmt::Display for Entry { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Entry::FreshId(label) => write!(f, "{}=*", label), + Entry::MapLabelToKey(label, key) => { + write!(f, "{}=@\"{}\"", label, key.replace("\"", "\"\"")) + } + Entry::GenericTuple(name, args) => { + write!(f, "{}(", name)?; + for (index, arg) in args.iter().enumerate() { + if index > 0 { + write!(f, ",")?; + } + write!(f, "{}", arg)?; + } + write!(f, ")") + } + Entry::Comment(line) => write!(f, "// {}", line), + } + } +} + +#[derive(Debug, Copy, Clone)] +// Identifiers of the form #0, #1... +pub struct Label(u32); + +impl fmt::Display for Label { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "#{:x}", self.0) + } +} + +// Some untyped argument to a TrapEntry. +#[derive(Debug, Clone)] +pub enum Arg { + Label(Label), + Int(usize), + String(String), +} + +const MAX_STRLEN: usize = 1048576; + +impl fmt::Display for Arg { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Arg::Label(x) => write!(f, "{}", x), + Arg::Int(x) => write!(f, "{}", x), + Arg::String(x) => write!( + f, + "\"{}\"", + limit_string(x, MAX_STRLEN).replace("\"", "\"\"") + ), + } + } +} + +pub struct Program(Vec); + +impl fmt::Display for Program { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut text = String::new(); + for trap_entry in &self.0 { + text.push_str(&format!("{}\n", trap_entry)); + } + write!(f, "{}", text) + } +} + +pub fn full_id_for_file(normalized_path: &str) -> String { + format!("{};sourcefile", escape_key(normalized_path)) +} + +pub fn full_id_for_folder(normalized_path: &str) -> String { + format!("{};folder", escape_key(normalized_path)) +} + +/// Escapes a string for use in a TRAP key, by replacing special characters with +/// HTML entities. +fn escape_key<'a, S: Into>>(key: S) -> Cow<'a, str> { + fn needs_escaping(c: char) -> bool { + matches!(c, '&' | '{' | '}' | '"' | '@' | '#') + } + + let key = key.into(); + if key.contains(needs_escaping) { + let mut escaped = String::with_capacity(2 * key.len()); + for c in key.chars() { + match c { + '&' => escaped.push_str("&"), + '{' => escaped.push_str("{"), + '}' => escaped.push_str("}"), + '"' => escaped.push_str("""), + '@' => escaped.push_str("@"), + '#' => escaped.push_str("#"), + _ => escaped.push(c), + } + } + Cow::Owned(escaped) + } else { + key + } +} + +/// Limit the length (in bytes) of a string. If the string's length in bytes is +/// less than or equal to the limit then the entire string is returned. Otherwise +/// the string is sliced at the provided limit. If there is a multi-byte character +/// at the limit then the returned slice will be slightly shorter than the limit to +/// avoid splitting that multi-byte character. +fn limit_string(string: &str, max_size: usize) -> &str { + if string.len() <= max_size { + return string; + } + let p = string.as_bytes(); + let mut index = max_size; + // We want to clip the string at [max_size]; however, the character at that position + // may span several bytes. We need to find the first byte of the character. In UTF-8 + // encoded data any byte that matches the bit pattern 10XXXXXX is not a start byte. + // Therefore we decrement the index as long as there are bytes matching this pattern. + // This ensures we cut the string at the border between one character and another. + while index > 0 && (p[index] & 0b11000000) == 0b10000000 { + index -= 1; + } + &string[0..index] +} + +#[derive(Clone, Copy)] +pub enum Compression { + None, + Gzip, +} + +impl Compression { + pub fn from_env(var_name: &str) -> Compression { + match std::env::var(var_name) { + Ok(method) => match Compression::from_string(&method) { + Some(c) => c, + None => { + tracing::error!("Unknown compression method '{}'; using gzip.", &method); + Compression::Gzip + } + }, + // Default compression method if the env var isn't set: + Err(_) => Compression::Gzip, + } + } + + pub fn from_string(s: &str) -> Option { + match s.to_lowercase().as_ref() { + "none" => Some(Compression::None), + "gzip" => Some(Compression::Gzip), + _ => None, + } + } + + pub fn extension(&self) -> &str { + match self { + Compression::None => "trap", + Compression::Gzip => "trap.gz", + } + } +} + +#[test] +fn limit_string_test() { + assert_eq!("hello", limit_string(&"hello world".to_owned(), 5)); + assert_eq!("hi ☹", limit_string(&"hi ☹☹".to_owned(), 6)); + assert_eq!("hi ", limit_string(&"hi ☹☹".to_owned(), 5)); +} + +#[test] +fn escape_key_test() { + assert_eq!("foo!", escape_key("foo!")); + assert_eq!("foo{}", escape_key("foo{}")); + assert_eq!("{}", escape_key("{}")); + assert_eq!("", escape_key("")); + assert_eq!("/path/to/foo.rb", escape_key("/path/to/foo.rb")); + assert_eq!( + "/path/to/foo&{}"@#.rb", + escape_key("/path/to/foo&{}\"@#.rb") + ); +} From 0a8ecd3cf73296779e92966682fced6793cdce33 Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Mon, 1 Nov 2021 16:31:30 +0000 Subject: [PATCH 308/505] Ruby: compute path string only once --- ruby/extractor/src/extractor.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/ruby/extractor/src/extractor.rs b/ruby/extractor/src/extractor.rs index 6c462e11bb4..74a8db28030 100644 --- a/ruby/extractor/src/extractor.rs +++ b/ruby/extractor/src/extractor.rs @@ -119,27 +119,28 @@ pub fn extract( source: &[u8], ranges: &[Range], ) -> std::io::Result<()> { + let path_str = format!("{}", path.display()); let span = span!( Level::TRACE, "extract", - file = %path.display() + file = %path_str ); let _enter = span.enter(); - info!("extracting: {}", path.display()); + info!("extracting: {}", path_str); let mut parser = Parser::new(); parser.set_language(language).unwrap(); parser.set_included_ranges(ranges).unwrap(); let tree = parser.parse(&source, None).expect("Failed to parse file"); - trap_writer.comment(format!("Auto-generated TRAP file for {}", path.display())); + trap_writer.comment(format!("Auto-generated TRAP file for {}", path_str)); let file_label = populate_file(trap_writer, path); let mut visitor = Visitor { source, trap_writer, // TODO: should we handle path strings that are not valid UTF8 better? - path: format!("{}", path.display()), + path: &path_str, file_label, toplevel_child_counter: 0, stack: Vec::new(), @@ -199,7 +200,7 @@ struct ChildNode { struct Visitor<'a> { /// The file path of the source code (as string) - path: String, + path: &'a str, /// The label to use whenever we need to refer to the `@file` entity of this /// source file. file_label: trap::Label, From 8dae85e1b1a249f9a2c0836eb184df26f4fadce5 Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Mon, 1 Nov 2021 17:19:26 +0000 Subject: [PATCH 309/505] Ruby: avoid repeated construction of table name strings --- ruby/extractor/src/extractor.rs | 44 ++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/ruby/extractor/src/extractor.rs b/ruby/extractor/src/extractor.rs index 74a8db28030..db280634ae5 100644 --- a/ruby/extractor/src/extractor.rs +++ b/ruby/extractor/src/extractor.rs @@ -136,17 +136,15 @@ pub fn extract( let tree = parser.parse(&source, None).expect("Failed to parse file"); trap_writer.comment(format!("Auto-generated TRAP file for {}", path_str)); let file_label = populate_file(trap_writer, path); - let mut visitor = Visitor { + let mut visitor = Visitor::new( source, trap_writer, // TODO: should we handle path strings that are not valid UTF8 better? - path: &path_str, + &path_str, file_label, - toplevel_child_counter: 0, - stack: Vec::new(), language_prefix, schema, - }; + ); traverse(&tree, &mut visitor); parser.reset(); @@ -210,8 +208,10 @@ struct Visitor<'a> { trap_writer: &'a mut trap::Writer, /// A counter for top-level child nodes toplevel_child_counter: usize, - /// Language prefix - language_prefix: &'a str, + /// Language-specific name of the AST info table + ast_node_info_table_name: String, + /// Language-specific name of the tokeninfo table + tokeninfo_table_name: String, /// A lookup table from type name to node types schema: &'a NodeTypeMap, /// A stack for gathering information from child nodes. Whenever a node is @@ -223,7 +223,28 @@ struct Visitor<'a> { stack: Vec<(trap::Label, usize, Vec)>, } -impl Visitor<'_> { +impl<'a> Visitor<'a> { + fn new( + source: &'a [u8], + trap_writer: &'a mut trap::Writer, + path: &'a str, + file_label: trap::Label, + language_prefix: &str, + schema: &'a NodeTypeMap, + ) -> Visitor<'a> { + Visitor { + path, + file_label, + source, + trap_writer, + toplevel_child_counter: 0, + ast_node_info_table_name: format!("{}_ast_node_info", language_prefix), + tokeninfo_table_name: format!("{}_tokeninfo", language_prefix), + schema, + stack: Vec::new(), + } + } + fn record_parse_error( &mut self, error_message: String, @@ -321,7 +342,7 @@ impl Visitor<'_> { match &table.kind { EntryKind::Token { kind_id, .. } => { self.trap_writer.add_tuple( - &format!("{}_ast_node_info", self.language_prefix), + &self.ast_node_info_table_name, vec![ trap::Arg::Label(id), trap::Arg::Label(parent_id), @@ -330,12 +351,11 @@ impl Visitor<'_> { ], ); self.trap_writer.add_tuple( - &format!("{}_tokeninfo", self.language_prefix), + &self.tokeninfo_table_name, vec![ trap::Arg::Label(id), trap::Arg::Int(*kind_id), sliced_source_arg(self.source, node), - trap::Arg::Label(loc), ], ); } @@ -345,7 +365,7 @@ impl Visitor<'_> { } => { if let Some(args) = self.complex_node(&node, fields, &child_nodes, id) { self.trap_writer.add_tuple( - &format!("{}_ast_node_info", self.language_prefix), + &self.ast_node_info_table_name, vec![ trap::Arg::Label(id), trap::Arg::Label(parent_id), From 7be106d7bba3cb89b55653570110766e86122f06 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Mon, 18 Jul 2022 15:01:25 +0200 Subject: [PATCH 310/505] Ruby: handle magic coding: comments --- ruby/Cargo.lock | Bin 15139 -> 17026 bytes ruby/extractor/Cargo.toml | 2 + ruby/extractor/src/main.rs | 207 +++++++++++++++++- ruby/ql/test/library-tests/ast/Ast.expected | 6 + .../library-tests/ast/TreeSitter.expected | 11 + .../test/library-tests/ast/ValueText.expected | 2 + .../library-tests/ast/misc/iso-8859-15.rb | 4 + 7 files changed, 231 insertions(+), 1 deletion(-) create mode 100644 ruby/ql/test/library-tests/ast/misc/iso-8859-15.rb diff --git a/ruby/Cargo.lock b/ruby/Cargo.lock index 1be66824a3e4184f0dabbebc3fec55848f3da549..c92ef0214a65f4bbdb4497085ba30c6a71d17e85 100644 GIT binary patch delta 1127 zcmaKrOHPzQ6oq3J`yIRO^5?&tLoOR$3Vpu=*%JBM_p~j2I-CwCq@%j@Vf!y z1~hELfh@+kKS(1F=}D#D=hSykeS7=q^QG_mi_2{+TRIv)A1u6WJCo7Yc;)`ezzxF? zpnG)X@p@vXHA2C}wL0}dfe{(7`WVe1dIV_bQ@_jA!d}6|lcbasQj?BWuYZ_{tc}Ln z-mSf)Z5p>p8{A!38Jt;rwYAe|{J)jS==HX|7+rL5(m!Ie@$U5ewTr#xbcgis>F(p| zvOA-C?C2x~KMc-CAAB9+0gHz&w!*<-3E6d_Vw zz&%cCCDJ@kXF>goSv&QkPG#xGy(bUUwcr#}DbhfQu{MTgK*1Ftl$_aOO~Sbr?E@yt zeO)0ysZM~r vR@Y}Zm~)tF_hxW&Z-4Rk((ReV;Xk!anznG*-K6)s8G@buOnYBy#X delta 25 gcmZo_Wn5gg;Ue#5VbQN5Y-u@(C8 usize { } } +lazy_static! { + static ref CP_NUMBER: regex::Regex = regex::Regex::new("cp([0-9]+)").unwrap(); +} + +fn encoding_from_name(encoding_name: &str) -> Option<&(dyn encoding::Encoding + Send + Sync)> { + match encoding::label::encoding_from_whatwg_label(&encoding_name) { + Some(e) => return Some(e), + None => { + if let Some(cap) = CP_NUMBER.captures(&encoding_name) { + return encoding::label::encoding_from_windows_code_page( + str::parse(cap.get(1).unwrap().as_str()).unwrap(), + ); + } else { + return None; + } + } + } +} + fn main() -> std::io::Result<()> { tracing_subscriber::fmt() .with_target(false) @@ -140,6 +163,7 @@ fn main() -> std::io::Result<()> { let path = PathBuf::from(line).canonicalize()?; let src_archive_file = path_for(&src_archive_dir, &path, ""); let mut source = std::fs::read(&path)?; + let mut needs_conversion = false; let code_ranges; let mut trap_writer = extractor::new_trap_writer(); if path.extension().map_or(false, |x| x == "erb") { @@ -168,6 +192,43 @@ fn main() -> std::io::Result<()> { } code_ranges = ranges; } else { + if let Some(encoding_name) = scan_coding_comment(&source) { + // If the input is already UTF-8 then there is no need to recode the source + // If the declared encoding is 'binary' or 'ascii-8bit' then it is not clear how + // to interpret characters. In this case it is probably best to leave the input + // unchanged. + if !encoding_name.eq_ignore_ascii_case("utf-8") + && !encoding_name.eq_ignore_ascii_case("ascii-8bit") + && !encoding_name.eq_ignore_ascii_case("binary") + { + if let Some(encoding) = encoding_from_name(&encoding_name) { + needs_conversion = + encoding.whatwg_name().unwrap_or_default() != "utf-8"; + if needs_conversion { + match encoding + .decode(&source, encoding::types::DecoderTrap::Replace) + { + Ok(str) => source = str.as_bytes().to_owned(), + Err(msg) => { + needs_conversion = false; + tracing::warn!( + "{}: character decoding failure: {} ({})", + &path.to_string_lossy(), + msg, + &encoding_name + ); + } + } + } + } else { + tracing::warn!( + "{}: unknown character encoding: '{}'", + &path.to_string_lossy(), + &encoding_name + ); + } + } + } code_ranges = vec![]; } extractor::extract( @@ -180,7 +241,11 @@ fn main() -> std::io::Result<()> { &code_ranges, )?; std::fs::create_dir_all(&src_archive_file.parent().unwrap())?; - std::fs::copy(&path, &src_archive_file)?; + if needs_conversion { + std::fs::write(&src_archive_file, &source)?; + } else { + std::fs::copy(&path, &src_archive_file)?; + } write_trap(&trap_dir, path, trap_writer, &trap_compression) }) .expect("failed to extract files"); @@ -299,3 +364,143 @@ fn path_for(dir: &Path, path: &Path, ext: &str) -> PathBuf { } result } + +fn skip_space(content: &[u8], index: usize) -> usize { + let mut index = index; + while index < content.len() { + let c = content[index] as char; + // white space except \n + let is_space = c == ' ' || ('\t'..='\r').contains(&c) && c != '\n'; + if !is_space { + break; + } + index += 1; + } + index +} + +fn scan_coding_comment(content: &[u8]) -> std::option::Option> { + let mut index = 0; + // skip UTF-8 BOM marker if there is one + if content.len() >= 3 && content[0] == 0xef && content[1] == 0xbb && content[2] == 0xbf { + index += 3; + } + // skip #! line if there is one + if index + 1 < content.len() + && content[index] as char == '#' + && content[index + 1] as char == '!' + { + index += 2; + while index < content.len() && content[index] as char != '\n' { + index += 1 + } + index += 1 + } + index = skip_space(content, index); + + if index >= content.len() || content[index] as char != '#' { + return None; + } + index += 1; + + const CODING: [char; 12] = ['C', 'c', 'O', 'o', 'D', 'd', 'I', 'i', 'N', 'n', 'G', 'g']; + let mut word_index = 0; + while index < content.len() && word_index < CODING.len() && content[index] as char != '\n' { + if content[index] as char == CODING[word_index] + || content[index] as char == CODING[word_index + 1] + { + word_index += 2 + } else { + word_index = 0; + } + index += 1; + } + if word_index < CODING.len() { + return None; + } + index = skip_space(content, index); + + if index < content.len() && content[index] as char != ':' && content[index] as char != '=' { + return None; + } + index += 1; + index = skip_space(content, index); + + let start = index; + while index < content.len() { + let c = content[index] as char; + if c == '-' || c == '_' || c.is_ascii_alphanumeric() { + index += 1; + } else { + break; + } + } + if index > start { + return Some(String::from_utf8_lossy(&content[start..index])); + } + None +} + +#[test] +fn test_scan_coding_comment() { + let text = "# encoding: utf-8"; + let result = scan_coding_comment(text.as_bytes()); + assert_eq!(result, Some("utf-8".into())); + + let text = "#coding:utf-8"; + let result = scan_coding_comment(&text.as_bytes()); + assert_eq!(result, Some("utf-8".into())); + + let text = "# foo\n# encoding: utf-8"; + let result = scan_coding_comment(&text.as_bytes()); + assert_eq!(result, None); + + let text = "# encoding: latin1 encoding: utf-8"; + let result = scan_coding_comment(&text.as_bytes()); + assert_eq!(result, Some("latin1".into())); + + let text = "# encoding: nonsense"; + let result = scan_coding_comment(&text.as_bytes()); + assert_eq!(result, Some("nonsense".into())); + + let text = "# coding = utf-8"; + let result = scan_coding_comment(&text.as_bytes()); + assert_eq!(result, Some("utf-8".into())); + + let text = "# CODING = utf-8"; + let result = scan_coding_comment(&text.as_bytes()); + assert_eq!(result, Some("utf-8".into())); + + let text = "# CoDiNg = utf-8"; + let result = scan_coding_comment(&text.as_bytes()); + assert_eq!(result, Some("utf-8".into())); + + let text = "# blah blahblahcoding = utf-8"; + let result = scan_coding_comment(&text.as_bytes()); + assert_eq!(result, Some("utf-8".into())); + + // unicode BOM is ignored + let text = "\u{FEFF}# encoding: utf-8"; + let result = scan_coding_comment(&text.as_bytes()); + assert_eq!(result, Some("utf-8".into())); + + let text = "\u{FEFF} # encoding: utf-8"; + let result = scan_coding_comment(&text.as_bytes()); + assert_eq!(result, Some("utf-8".into())); + + let text = "#! /usr/bin/env ruby\n # encoding: utf-8"; + let result = scan_coding_comment(&text.as_bytes()); + assert_eq!(result, Some("utf-8".into())); + + let text = "\u{FEFF}#! /usr/bin/env ruby\n # encoding: utf-8"; + let result = scan_coding_comment(&text.as_bytes()); + assert_eq!(result, Some("utf-8".into())); + + // A #! must be the first thing on a line, otherwise it's a normal comment + let text = " #! /usr/bin/env ruby encoding = utf-8"; + let result = scan_coding_comment(&text.as_bytes()); + assert_eq!(result, Some("utf-8".into())); + let text = " #! /usr/bin/env ruby \n # encoding = utf-8"; + let result = scan_coding_comment(&text.as_bytes()); + assert_eq!(result, None); +} diff --git a/ruby/ql/test/library-tests/ast/Ast.expected b/ruby/ql/test/library-tests/ast/Ast.expected index 0a16654594d..4cb29e0f033 100644 --- a/ruby/ql/test/library-tests/ast/Ast.expected +++ b/ruby/ql/test/library-tests/ast/Ast.expected @@ -1762,6 +1762,12 @@ escape_sequences/escapes.rb: # 93| getStmt: [SymbolLiteral] :"\C-?" # 93| getComponent: [StringEscapeSequenceComponent] \C # 93| getComponent: [StringTextComponent] -? +misc/iso-8859-15.rb: +# 1| [Toplevel] iso-8859-15.rb +# 4| getStmt: [MethodCall] call to print +# 4| getReceiver: [SelfVariableAccess] self +# 4| getArgument: [StringLiteral] "EUR = €" +# 4| getComponent: [StringTextComponent] EUR = € literals/literals.rb: # 1| [Toplevel] literals.rb # 2| getStmt: [NilLiteral] nil diff --git a/ruby/ql/test/library-tests/ast/TreeSitter.expected b/ruby/ql/test/library-tests/ast/TreeSitter.expected index 67a909d9002..cb5a17ebebb 100644 --- a/ruby/ql/test/library-tests/ast/TreeSitter.expected +++ b/ruby/ql/test/library-tests/ast/TreeSitter.expected @@ -4604,6 +4604,17 @@ literals/literals.rb: # 193| cat file.txt # 193| # 195| 1: [HeredocEnd] SCRIPT +misc/iso-8859-15.rb: +# 1| [Program] Program +# 4| 0: [Call] Call +# 4| 0: [Identifier] print +# 4| 1: [ArgumentList] ArgumentList +# 4| 0: [String] String +# 4| 0: [ReservedWord] " +# 4| 1: [StringContent] EUR = € +# 4| 2: [ReservedWord] " +# 1| [Comment] #! /usr/bin/ruby +# 2| [Comment] # coding: iso-8859-15 misc/misc.erb: # 2| [Program] Program # 2| 0: [Call] Call diff --git a/ruby/ql/test/library-tests/ast/ValueText.expected b/ruby/ql/test/library-tests/ast/ValueText.expected index ecf7399a99a..e66838fbe2c 100644 --- a/ruby/ql/test/library-tests/ast/ValueText.expected +++ b/ruby/ql/test/library-tests/ast/ValueText.expected @@ -717,6 +717,7 @@ exprValue | literals/literals.rb:198:8:198:8 | 5 | 5 | int | | literals/literals.rb:199:2:199:2 | :y | :y | symbol | | literals/literals.rb:199:7:199:7 | :Z | :Z | symbol | +| misc/iso-8859-15.rb:4:7:4:17 | "EUR = \u20ac" | EUR = \u20ac | string | | misc/misc.erb:2:15:2:37 | "main_include_admin.js" | main_include_admin.js | string | | misc/misc.rb:1:7:1:11 | "bar" | bar | string | | misc/misc.rb:3:7:3:9 | foo | foo | string | @@ -1592,6 +1593,7 @@ exprCfgNodeValue | literals/literals.rb:198:8:198:8 | 5 | 5 | int | | literals/literals.rb:199:2:199:2 | :y | :y | symbol | | literals/literals.rb:199:7:199:7 | :Z | :Z | symbol | +| misc/iso-8859-15.rb:4:7:4:17 | "EUR = \u20ac" | EUR = \u20ac | string | | misc/misc.erb:2:15:2:37 | "main_include_admin.js" | main_include_admin.js | string | | misc/misc.rb:1:7:1:11 | "bar" | bar | string | | misc/misc.rb:3:7:3:9 | foo | foo | string | diff --git a/ruby/ql/test/library-tests/ast/misc/iso-8859-15.rb b/ruby/ql/test/library-tests/ast/misc/iso-8859-15.rb new file mode 100644 index 00000000000..d5fd8ae9456 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/misc/iso-8859-15.rb @@ -0,0 +1,4 @@ +#! /usr/bin/ruby +# coding: iso-8859-15 + +print "EUR = ¤" \ No newline at end of file From 5f96c92fac08025fad9098442ea370308dc78201 Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Thu, 21 Jul 2022 17:38:33 +0100 Subject: [PATCH 311/505] QL: sync Ruby extractor changes --- ql/extractor/src/extractor.rs | 505 +++++++++++----------------------- ql/extractor/src/main.rs | 66 +---- ql/extractor/src/trap.rs | 272 ++++++++++++++++++ 3 files changed, 446 insertions(+), 397 deletions(-) create mode 100644 ql/extractor/src/trap.rs diff --git a/ql/extractor/src/extractor.rs b/ql/extractor/src/extractor.rs index 8cdaff1b738..db280634ae5 100644 --- a/ql/extractor/src/extractor.rs +++ b/ql/extractor/src/extractor.rs @@ -1,161 +1,112 @@ +use crate::trap; use node_types::{EntryKind, Field, NodeTypeMap, Storage, TypeName}; -use std::borrow::Cow; use std::collections::BTreeMap as Map; use std::collections::BTreeSet as Set; use std::fmt; -use std::io::Write; use std::path::Path; use tracing::{error, info, span, Level}; use tree_sitter::{Language, Node, Parser, Range, Tree}; -pub struct TrapWriter { - /// The accumulated trap entries - trap_output: Vec, - /// A counter for generating fresh labels - counter: u32, - /// cache of global keys - global_keys: std::collections::HashMap, +pub fn populate_file(writer: &mut trap::Writer, absolute_path: &Path) -> trap::Label { + let (file_label, fresh) = + writer.global_id(&trap::full_id_for_file(&normalize_path(absolute_path))); + if fresh { + writer.add_tuple( + "files", + vec![ + trap::Arg::Label(file_label), + trap::Arg::String(normalize_path(absolute_path)), + ], + ); + populate_parent_folders(writer, file_label, absolute_path.parent()); + } + file_label } -pub fn new_trap_writer() -> TrapWriter { - TrapWriter { - counter: 0, - trap_output: Vec::new(), - global_keys: std::collections::HashMap::new(), +fn populate_empty_file(writer: &mut trap::Writer) -> trap::Label { + let (file_label, fresh) = writer.global_id("empty;sourcefile"); + if fresh { + writer.add_tuple( + "files", + vec![ + trap::Arg::Label(file_label), + trap::Arg::String("".to_string()), + ], + ); } + file_label } -impl TrapWriter { - /// Gets a label that will hold the unique ID of the passed string at import time. - /// This can be used for incrementally importable TRAP files -- use globally unique - /// strings to compute a unique ID for table tuples. - /// - /// Note: You probably want to make sure that the key strings that you use are disjoint - /// for disjoint column types; the standard way of doing this is to prefix (or append) - /// the column type name to the ID. Thus, you might identify methods in Java by the - /// full ID "methods_com.method.package.DeclaringClass.method(argumentList)". +pub fn populate_empty_location(writer: &mut trap::Writer) { + let file_label = populate_empty_file(writer); + location(writer, file_label, 0, 0, 0, 0); +} - fn fresh_id(&mut self) -> Label { - let label = Label(self.counter); - self.counter += 1; - self.trap_output.push(TrapEntry::FreshId(label)); - label - } - - fn global_id(&mut self, key: &str) -> (Label, bool) { - if let Some(label) = self.global_keys.get(key) { - return (*label, false); - } - let label = Label(self.counter); - self.counter += 1; - self.global_keys.insert(key.to_owned(), label); - self.trap_output - .push(TrapEntry::MapLabelToKey(label, key.to_owned())); - (label, true) - } - - fn add_tuple(&mut self, table_name: &str, args: Vec) { - self.trap_output - .push(TrapEntry::GenericTuple(table_name.to_owned(), args)) - } - - fn populate_file(&mut self, absolute_path: &Path) -> Label { - let (file_label, fresh) = self.global_id(&full_id_for_file(absolute_path)); - if fresh { - self.add_tuple( - "files", - vec![ - Arg::Label(file_label), - Arg::String(normalize_path(absolute_path)), - ], - ); - self.populate_parent_folders(file_label, absolute_path.parent()); - } - file_label - } - - fn populate_empty_file(&mut self) -> Label { - let (file_label, fresh) = self.global_id("empty;sourcefile"); - if fresh { - self.add_tuple( - "files", - vec![Arg::Label(file_label), Arg::String("".to_string())], - ); - } - file_label - } - - pub fn populate_empty_location(&mut self) { - let file_label = self.populate_empty_file(); - self.location(file_label, 0, 0, 0, 0); - } - - fn populate_parent_folders(&mut self, child_label: Label, path: Option<&Path>) { - let mut path = path; - let mut child_label = child_label; - loop { - match path { - None => break, - Some(folder) => { - let (folder_label, fresh) = self.global_id(&full_id_for_folder(folder)); - self.add_tuple( - "containerparent", - vec![Arg::Label(folder_label), Arg::Label(child_label)], +pub fn populate_parent_folders( + writer: &mut trap::Writer, + child_label: trap::Label, + path: Option<&Path>, +) { + let mut path = path; + let mut child_label = child_label; + loop { + match path { + None => break, + Some(folder) => { + let (folder_label, fresh) = + writer.global_id(&trap::full_id_for_folder(&normalize_path(folder))); + writer.add_tuple( + "containerparent", + vec![ + trap::Arg::Label(folder_label), + trap::Arg::Label(child_label), + ], + ); + if fresh { + writer.add_tuple( + "folders", + vec![ + trap::Arg::Label(folder_label), + trap::Arg::String(normalize_path(folder)), + ], ); - if fresh { - self.add_tuple( - "folders", - vec![ - Arg::Label(folder_label), - Arg::String(normalize_path(folder)), - ], - ); - path = folder.parent(); - child_label = folder_label; - } else { - break; - } + path = folder.parent(); + child_label = folder_label; + } else { + break; } } } } +} - fn location( - &mut self, - file_label: Label, - start_line: usize, - start_column: usize, - end_line: usize, - end_column: usize, - ) -> Label { - let (loc_label, fresh) = self.global_id(&format!( - "loc,{{{}}},{},{},{},{}", - file_label, start_line, start_column, end_line, end_column - )); - if fresh { - self.add_tuple( - "locations_default", - vec![ - Arg::Label(loc_label), - Arg::Label(file_label), - Arg::Int(start_line), - Arg::Int(start_column), - Arg::Int(end_line), - Arg::Int(end_column), - ], - ); - } - loc_label - } - - fn comment(&mut self, text: String) { - self.trap_output.push(TrapEntry::Comment(text)); - } - - pub fn output(self, writer: &mut dyn Write) -> std::io::Result<()> { - write!(writer, "{}", Program(self.trap_output)) +fn location( + writer: &mut trap::Writer, + file_label: trap::Label, + start_line: usize, + start_column: usize, + end_line: usize, + end_column: usize, +) -> trap::Label { + let (loc_label, fresh) = writer.global_id(&format!( + "loc,{{{}}},{},{},{},{}", + file_label, start_line, start_column, end_line, end_column + )); + if fresh { + writer.add_tuple( + "locations_default", + vec![ + trap::Arg::Label(loc_label), + trap::Arg::Label(file_label), + trap::Arg::Int(start_line), + trap::Arg::Int(start_column), + trap::Arg::Int(end_line), + trap::Arg::Int(end_column), + ], + ); } + loc_label } /// Extracts the source file at `path`, which is assumed to be canonicalized. @@ -163,71 +114,43 @@ pub fn extract( language: Language, language_prefix: &str, schema: &NodeTypeMap, - trap_writer: &mut TrapWriter, + trap_writer: &mut trap::Writer, path: &Path, source: &[u8], ranges: &[Range], ) -> std::io::Result<()> { + let path_str = format!("{}", path.display()); let span = span!( Level::TRACE, "extract", - file = %path.display() + file = %path_str ); let _enter = span.enter(); - info!("extracting: {}", path.display()); + info!("extracting: {}", path_str); let mut parser = Parser::new(); parser.set_language(language).unwrap(); parser.set_included_ranges(ranges).unwrap(); let tree = parser.parse(&source, None).expect("Failed to parse file"); - trap_writer.comment(format!("Auto-generated TRAP file for {}", path.display())); - let file_label = &trap_writer.populate_file(path); - let mut visitor = Visitor { + trap_writer.comment(format!("Auto-generated TRAP file for {}", path_str)); + let file_label = populate_file(trap_writer, path); + let mut visitor = Visitor::new( source, trap_writer, // TODO: should we handle path strings that are not valid UTF8 better? - path: format!("{}", path.display()), - file_label: *file_label, - toplevel_child_counter: 0, - stack: Vec::new(), + &path_str, + file_label, language_prefix, schema, - }; + ); traverse(&tree, &mut visitor); parser.reset(); Ok(()) } -/// Escapes a string for use in a TRAP key, by replacing special characters with -/// HTML entities. -fn escape_key<'a, S: Into>>(key: S) -> Cow<'a, str> { - fn needs_escaping(c: char) -> bool { - matches!(c, '&' | '{' | '}' | '"' | '@' | '#') - } - - let key = key.into(); - if key.contains(needs_escaping) { - let mut escaped = String::with_capacity(2 * key.len()); - for c in key.chars() { - match c { - '&' => escaped.push_str("&"), - '{' => escaped.push_str("{"), - '}' => escaped.push_str("}"), - '"' => escaped.push_str("""), - '@' => escaped.push_str("@"), - '#' => escaped.push_str("#"), - _ => escaped.push(c), - } - } - Cow::Owned(escaped) - } else { - key - } -} - /// Normalizes the path according the common CodeQL specification. Assumes that /// `path` has already been canonicalized using `std::fs::canonicalize`. fn normalize_path(path: &Path) -> String { @@ -267,34 +190,28 @@ fn normalize_path(path: &Path) -> String { } } -fn full_id_for_file(path: &Path) -> String { - format!("{};sourcefile", escape_key(&normalize_path(path))) -} - -fn full_id_for_folder(path: &Path) -> String { - format!("{};folder", escape_key(&normalize_path(path))) -} - struct ChildNode { field_name: Option<&'static str>, - label: Label, + label: trap::Label, type_name: TypeName, } struct Visitor<'a> { /// The file path of the source code (as string) - path: String, + path: &'a str, /// The label to use whenever we need to refer to the `@file` entity of this /// source file. - file_label: Label, + file_label: trap::Label, /// The source code as a UTF-8 byte array source: &'a [u8], - /// A TrapWriter to accumulate trap entries - trap_writer: &'a mut TrapWriter, + /// A trap::Writer to accumulate trap entries + trap_writer: &'a mut trap::Writer, /// A counter for top-level child nodes toplevel_child_counter: usize, - /// Language prefix - language_prefix: &'a str, + /// Language-specific name of the AST info table + ast_node_info_table_name: String, + /// Language-specific name of the tokeninfo table + tokeninfo_table_name: String, /// A lookup table from type name to node types schema: &'a NodeTypeMap, /// A stack for gathering information from child nodes. Whenever a node is @@ -303,27 +220,48 @@ struct Visitor<'a> { /// node the list containing the child data is popped from the stack and /// matched against the dbscheme for the node. If the expectations are met /// the corresponding row definitions are added to the trap_output. - stack: Vec<(Label, usize, Vec)>, + stack: Vec<(trap::Label, usize, Vec)>, } -impl Visitor<'_> { +impl<'a> Visitor<'a> { + fn new( + source: &'a [u8], + trap_writer: &'a mut trap::Writer, + path: &'a str, + file_label: trap::Label, + language_prefix: &str, + schema: &'a NodeTypeMap, + ) -> Visitor<'a> { + Visitor { + path, + file_label, + source, + trap_writer, + toplevel_child_counter: 0, + ast_node_info_table_name: format!("{}_ast_node_info", language_prefix), + tokeninfo_table_name: format!("{}_tokeninfo", language_prefix), + schema, + stack: Vec::new(), + } + } + fn record_parse_error( &mut self, error_message: String, full_error_message: String, - loc: Label, + loc: trap::Label, ) { error!("{}", full_error_message); let id = self.trap_writer.fresh_id(); self.trap_writer.add_tuple( "diagnostics", vec![ - Arg::Label(id), - Arg::Int(40), // severity 40 = error - Arg::String("parse_error".to_string()), - Arg::String(error_message), - Arg::String(full_error_message), - Arg::Label(loc), + trap::Arg::Label(id), + trap::Arg::Int(40), // severity 40 = error + trap::Arg::String("parse_error".to_string()), + trap::Arg::String(error_message), + trap::Arg::String(full_error_message), + trap::Arg::Label(loc), ], ); } @@ -335,7 +273,8 @@ impl Visitor<'_> { node: Node, ) { let (start_line, start_column, end_line, end_column) = location_for(self.source, node); - let loc = self.trap_writer.location( + let loc = location( + self.trap_writer, self.file_label, start_line, start_column, @@ -374,7 +313,8 @@ impl Visitor<'_> { } let (id, _, child_nodes) = self.stack.pop().expect("Vistor: empty stack"); let (start_line, start_column, end_line, end_column) = location_for(self.source, node); - let loc = self.trap_writer.location( + let loc = location( + self.trap_writer, self.file_label, start_line, start_column, @@ -402,19 +342,19 @@ impl Visitor<'_> { match &table.kind { EntryKind::Token { kind_id, .. } => { self.trap_writer.add_tuple( - &format!("{}_ast_node_info", self.language_prefix), + &self.ast_node_info_table_name, vec![ - Arg::Label(id), - Arg::Label(parent_id), - Arg::Int(parent_index), - Arg::Label(loc), + trap::Arg::Label(id), + trap::Arg::Label(parent_id), + trap::Arg::Int(parent_index), + trap::Arg::Label(loc), ], ); self.trap_writer.add_tuple( - &format!("{}_tokeninfo", self.language_prefix), + &self.tokeninfo_table_name, vec![ - Arg::Label(id), - Arg::Int(*kind_id), + trap::Arg::Label(id), + trap::Arg::Int(*kind_id), sliced_source_arg(self.source, node), ], ); @@ -425,15 +365,15 @@ impl Visitor<'_> { } => { if let Some(args) = self.complex_node(&node, fields, &child_nodes, id) { self.trap_writer.add_tuple( - &format!("{}_ast_node_info", self.language_prefix), + &self.ast_node_info_table_name, vec![ - Arg::Label(id), - Arg::Label(parent_id), - Arg::Int(parent_index), - Arg::Label(loc), + trap::Arg::Label(id), + trap::Arg::Label(parent_id), + trap::Arg::Int(parent_index), + trap::Arg::Label(loc), ], ); - let mut all_args = vec![Arg::Label(id)]; + let mut all_args = vec![trap::Arg::Label(id)]; all_args.extend(args); self.trap_writer.add_tuple(table_name, all_args); } @@ -472,9 +412,9 @@ impl Visitor<'_> { node: &Node, fields: &[Field], child_nodes: &[ChildNode], - parent_id: Label, - ) -> Option> { - let mut map: Map<&Option, (&Field, Vec)> = Map::new(); + parent_id: trap::Label, + ) -> Option> { + let mut map: Map<&Option, (&Field, Vec)> = Map::new(); for field in fields { map.insert(&field.name, (field, Vec::new())); } @@ -488,9 +428,9 @@ impl Visitor<'_> { { // We can safely unwrap because type_matches checks the key is in the map. let (int_value, _) = int_mapping.get(&child_node.type_name.kind).unwrap(); - values.push(Arg::Int(*int_value)); + values.push(trap::Arg::Int(*int_value)); } else { - values.push(Arg::Label(child_node.label)); + values.push(trap::Arg::Label(child_node.label)); } } else if field.name.is_some() { let error_message = format!( @@ -569,9 +509,9 @@ impl Visitor<'_> { ); break; } - let mut args = vec![Arg::Label(parent_id)]; + let mut args = vec![trap::Arg::Label(parent_id)]; if *has_index { - args.push(Arg::Int(index)) + args.push(trap::Arg::Int(index)) } args.push(child_value.clone()); self.trap_writer.add_tuple(table_name, args); @@ -625,9 +565,9 @@ impl Visitor<'_> { } // Emit a slice of a source file as an Arg. -fn sliced_source_arg(source: &[u8], n: Node) -> Arg { +fn sliced_source_arg(source: &[u8], n: Node) -> trap::Arg { let range = n.byte_range(); - Arg::String(String::from_utf8_lossy(&source[range.start..range.end]).into_owned()) + trap::Arg::String(String::from_utf8_lossy(&source[range.start..range.end]).into_owned()) } // Emit a pair of `TrapEntry`s for the provided node, appropriately calibrated. @@ -699,59 +639,6 @@ fn traverse(tree: &Tree, visitor: &mut Visitor) { } } -pub struct Program(Vec); - -impl fmt::Display for Program { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut text = String::new(); - for trap_entry in &self.0 { - text.push_str(&format!("{}\n", trap_entry)); - } - write!(f, "{}", text) - } -} - -enum TrapEntry { - /// Maps the label to a fresh id, e.g. `#123=*`. - FreshId(Label), - /// Maps the label to a key, e.g. `#7=@"foo"`. - MapLabelToKey(Label, String), - /// foo_bar(arg*) - GenericTuple(String, Vec), - Comment(String), -} -impl fmt::Display for TrapEntry { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - TrapEntry::FreshId(label) => write!(f, "{}=*", label), - TrapEntry::MapLabelToKey(label, key) => { - write!(f, "{}=@\"{}\"", label, key.replace("\"", "\"\"")) - } - TrapEntry::GenericTuple(name, args) => { - write!(f, "{}(", name)?; - for (index, arg) in args.iter().enumerate() { - if index > 0 { - write!(f, ",")?; - } - write!(f, "{}", arg)?; - } - write!(f, ")") - } - TrapEntry::Comment(line) => write!(f, "// {}", line), - } - } -} - -#[derive(Debug, Copy, Clone)] -// Identifiers of the form #0, #1... -struct Label(u32); - -impl fmt::Display for Label { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "#{:x}", self.0) - } -} - // Numeric indices. #[derive(Debug, Copy, Clone)] struct Index(usize); @@ -761,69 +648,3 @@ impl fmt::Display for Index { write!(f, "{}", self.0) } } - -// Some untyped argument to a TrapEntry. -#[derive(Debug, Clone)] -enum Arg { - Label(Label), - Int(usize), - String(String), -} - -const MAX_STRLEN: usize = 1048576; - -impl fmt::Display for Arg { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Arg::Label(x) => write!(f, "{}", x), - Arg::Int(x) => write!(f, "{}", x), - Arg::String(x) => write!( - f, - "\"{}\"", - limit_string(x, MAX_STRLEN).replace("\"", "\"\"") - ), - } - } -} - -/// Limit the length (in bytes) of a string. If the string's length in bytes is -/// less than or equal to the limit then the entire string is returned. Otherwise -/// the string is sliced at the provided limit. If there is a multi-byte character -/// at the limit then the returned slice will be slightly shorter than the limit to -/// avoid splitting that multi-byte character. -fn limit_string(string: &str, max_size: usize) -> &str { - if string.len() <= max_size { - return string; - } - let p = string.as_bytes(); - let mut index = max_size; - // We want to clip the string at [max_size]; however, the character at that position - // may span several bytes. We need to find the first byte of the character. In UTF-8 - // encoded data any byte that matches the bit pattern 10XXXXXX is not a start byte. - // Therefore we decrement the index as long as there are bytes matching this pattern. - // This ensures we cut the string at the border between one character and another. - while index > 0 && (p[index] & 0b11000000) == 0b10000000 { - index -= 1; - } - &string[0..index] -} - -#[test] -fn limit_string_test() { - assert_eq!("hello", limit_string(&"hello world".to_owned(), 5)); - assert_eq!("hi ☹", limit_string(&"hi ☹☹".to_owned(), 6)); - assert_eq!("hi ", limit_string(&"hi ☹☹".to_owned(), 5)); -} - -#[test] -fn escape_key_test() { - assert_eq!("foo!", escape_key("foo!")); - assert_eq!("foo{}", escape_key("foo{}")); - assert_eq!("{}", escape_key("{}")); - assert_eq!("", escape_key("")); - assert_eq!("/path/to/foo.rb", escape_key("/path/to/foo.rb")); - assert_eq!( - "/path/to/foo&{}"@#.rb", - escape_key("/path/to/foo&{}\"@#.rb") - ); -} diff --git a/ql/extractor/src/main.rs b/ql/extractor/src/main.rs index 7f8ab87a7ca..9584304c430 100644 --- a/ql/extractor/src/main.rs +++ b/ql/extractor/src/main.rs @@ -1,49 +1,13 @@ mod extractor; +mod trap; extern crate num_cpus; -use flate2::write::GzEncoder; use rayon::prelude::*; use std::fs; -use std::io::{BufRead, BufWriter}; +use std::io::BufRead; use std::path::{Path, PathBuf}; -enum TrapCompression { - None, - Gzip, -} - -impl TrapCompression { - fn from_env() -> TrapCompression { - match std::env::var("CODEQL_QL_TRAP_COMPRESSION") { - Ok(method) => match TrapCompression::from_string(&method) { - Some(c) => c, - None => { - tracing::error!("Unknown compression method '{}'; using gzip.", &method); - TrapCompression::Gzip - } - }, - // Default compression method if the env var isn't set: - Err(_) => TrapCompression::Gzip, - } - } - - fn from_string(s: &str) -> Option { - match s.to_lowercase().as_ref() { - "none" => Some(TrapCompression::None), - "gzip" => Some(TrapCompression::Gzip), - _ => None, - } - } - - fn extension(&self) -> &str { - match self { - TrapCompression::None => "trap", - TrapCompression::Gzip => "trap.gz", - } - } -} - /** * Gets the number of threads the extractor should use, by reading the * CODEQL_THREADS environment variable and using it as described in the @@ -115,7 +79,7 @@ fn main() -> std::io::Result<()> { .value_of("output-dir") .expect("missing --output-dir"); let trap_dir = PathBuf::from(trap_dir); - let trap_compression = TrapCompression::from_env(); + let trap_compression = trap::Compression::from_env("CODEQL_QL_TRAP_COMPRESSION"); let file_list = matches.value_of("file-list").expect("missing --file-list"); let file_list = fs::File::open(file_list)?; @@ -140,7 +104,7 @@ fn main() -> std::io::Result<()> { let src_archive_file = path_for(&src_archive_dir, &path, ""); let source = std::fs::read(&path)?; let code_ranges = vec![]; - let mut trap_writer = extractor::new_trap_writer(); + let mut trap_writer = trap::Writer::new(); extractor::extract( language, "ql", @@ -152,33 +116,25 @@ fn main() -> std::io::Result<()> { )?; std::fs::create_dir_all(&src_archive_file.parent().unwrap())?; std::fs::copy(&path, &src_archive_file)?; - write_trap(&trap_dir, path, trap_writer, &trap_compression) + write_trap(&trap_dir, path, &trap_writer, trap_compression) }) .expect("failed to extract files"); let path = PathBuf::from("extras"); - let mut trap_writer = extractor::new_trap_writer(); - trap_writer.populate_empty_location(); - write_trap(&trap_dir, path, trap_writer, &trap_compression) + let mut trap_writer = trap::Writer::new(); + extractor::populate_empty_location(&mut trap_writer); + write_trap(&trap_dir, path, &trap_writer, trap_compression) } fn write_trap( trap_dir: &Path, path: PathBuf, - trap_writer: extractor::TrapWriter, - trap_compression: &TrapCompression, + trap_writer: &trap::Writer, + trap_compression: trap::Compression, ) -> std::io::Result<()> { let trap_file = path_for(trap_dir, &path, trap_compression.extension()); std::fs::create_dir_all(&trap_file.parent().unwrap())?; - let trap_file = std::fs::File::create(&trap_file)?; - let mut trap_file = BufWriter::new(trap_file); - match trap_compression { - TrapCompression::None => trap_writer.output(&mut trap_file), - TrapCompression::Gzip => { - let mut compressed_writer = GzEncoder::new(trap_file, flate2::Compression::fast()); - trap_writer.output(&mut compressed_writer) - } - } + trap_writer.write_to_file(&trap_file, trap_compression) } fn path_for(dir: &Path, path: &Path, ext: &str) -> PathBuf { diff --git a/ql/extractor/src/trap.rs b/ql/extractor/src/trap.rs new file mode 100644 index 00000000000..d64c520c4cc --- /dev/null +++ b/ql/extractor/src/trap.rs @@ -0,0 +1,272 @@ +use std::borrow::Cow; +use std::fmt; +use std::io::{BufWriter, Write}; +use std::path::Path; + +use flate2::write::GzEncoder; + +pub struct Writer { + /// The accumulated trap entries + trap_output: Vec, + /// A counter for generating fresh labels + counter: u32, + /// cache of global keys + global_keys: std::collections::HashMap, +} + +impl Writer { + pub fn new() -> Writer { + Writer { + counter: 0, + trap_output: Vec::new(), + global_keys: std::collections::HashMap::new(), + } + } + + pub fn fresh_id(&mut self) -> Label { + let label = Label(self.counter); + self.counter += 1; + self.trap_output.push(Entry::FreshId(label)); + label + } + + /// Gets a label that will hold the unique ID of the passed string at import time. + /// This can be used for incrementally importable TRAP files -- use globally unique + /// strings to compute a unique ID for table tuples. + /// + /// Note: You probably want to make sure that the key strings that you use are disjoint + /// for disjoint column types; the standard way of doing this is to prefix (or append) + /// the column type name to the ID. Thus, you might identify methods in Java by the + /// full ID "methods_com.method.package.DeclaringClass.method(argumentList)". + pub fn global_id(&mut self, key: &str) -> (Label, bool) { + if let Some(label) = self.global_keys.get(key) { + return (*label, false); + } + let label = Label(self.counter); + self.counter += 1; + self.global_keys.insert(key.to_owned(), label); + self.trap_output + .push(Entry::MapLabelToKey(label, key.to_owned())); + (label, true) + } + + pub fn add_tuple(&mut self, table_name: &str, args: Vec) { + self.trap_output + .push(Entry::GenericTuple(table_name.to_owned(), args)) + } + + pub fn comment(&mut self, text: String) { + self.trap_output.push(Entry::Comment(text)); + } + + pub fn write_to_file(&self, path: &Path, compression: Compression) -> std::io::Result<()> { + let trap_file = std::fs::File::create(path)?; + let mut trap_file = BufWriter::new(trap_file); + match compression { + Compression::None => self.write_trap_entries(&mut trap_file), + Compression::Gzip => { + let mut compressed_writer = GzEncoder::new(trap_file, flate2::Compression::fast()); + self.write_trap_entries(&mut compressed_writer) + } + } + } + + fn write_trap_entries(&self, file: &mut W) -> std::io::Result<()> { + for trap_entry in &self.trap_output { + writeln!(file, "{}", trap_entry)?; + } + std::io::Result::Ok(()) + } +} + +pub enum Entry { + /// Maps the label to a fresh id, e.g. `#123=*`. + FreshId(Label), + /// Maps the label to a key, e.g. `#7=@"foo"`. + MapLabelToKey(Label, String), + /// foo_bar(arg*) + GenericTuple(String, Vec), + Comment(String), +} + +impl fmt::Display for Entry { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Entry::FreshId(label) => write!(f, "{}=*", label), + Entry::MapLabelToKey(label, key) => { + write!(f, "{}=@\"{}\"", label, key.replace("\"", "\"\"")) + } + Entry::GenericTuple(name, args) => { + write!(f, "{}(", name)?; + for (index, arg) in args.iter().enumerate() { + if index > 0 { + write!(f, ",")?; + } + write!(f, "{}", arg)?; + } + write!(f, ")") + } + Entry::Comment(line) => write!(f, "// {}", line), + } + } +} + +#[derive(Debug, Copy, Clone)] +// Identifiers of the form #0, #1... +pub struct Label(u32); + +impl fmt::Display for Label { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "#{:x}", self.0) + } +} + +// Some untyped argument to a TrapEntry. +#[derive(Debug, Clone)] +pub enum Arg { + Label(Label), + Int(usize), + String(String), +} + +const MAX_STRLEN: usize = 1048576; + +impl fmt::Display for Arg { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Arg::Label(x) => write!(f, "{}", x), + Arg::Int(x) => write!(f, "{}", x), + Arg::String(x) => write!( + f, + "\"{}\"", + limit_string(x, MAX_STRLEN).replace("\"", "\"\"") + ), + } + } +} + +pub struct Program(Vec); + +impl fmt::Display for Program { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut text = String::new(); + for trap_entry in &self.0 { + text.push_str(&format!("{}\n", trap_entry)); + } + write!(f, "{}", text) + } +} + +pub fn full_id_for_file(normalized_path: &str) -> String { + format!("{};sourcefile", escape_key(normalized_path)) +} + +pub fn full_id_for_folder(normalized_path: &str) -> String { + format!("{};folder", escape_key(normalized_path)) +} + +/// Escapes a string for use in a TRAP key, by replacing special characters with +/// HTML entities. +fn escape_key<'a, S: Into>>(key: S) -> Cow<'a, str> { + fn needs_escaping(c: char) -> bool { + matches!(c, '&' | '{' | '}' | '"' | '@' | '#') + } + + let key = key.into(); + if key.contains(needs_escaping) { + let mut escaped = String::with_capacity(2 * key.len()); + for c in key.chars() { + match c { + '&' => escaped.push_str("&"), + '{' => escaped.push_str("{"), + '}' => escaped.push_str("}"), + '"' => escaped.push_str("""), + '@' => escaped.push_str("@"), + '#' => escaped.push_str("#"), + _ => escaped.push(c), + } + } + Cow::Owned(escaped) + } else { + key + } +} + +/// Limit the length (in bytes) of a string. If the string's length in bytes is +/// less than or equal to the limit then the entire string is returned. Otherwise +/// the string is sliced at the provided limit. If there is a multi-byte character +/// at the limit then the returned slice will be slightly shorter than the limit to +/// avoid splitting that multi-byte character. +fn limit_string(string: &str, max_size: usize) -> &str { + if string.len() <= max_size { + return string; + } + let p = string.as_bytes(); + let mut index = max_size; + // We want to clip the string at [max_size]; however, the character at that position + // may span several bytes. We need to find the first byte of the character. In UTF-8 + // encoded data any byte that matches the bit pattern 10XXXXXX is not a start byte. + // Therefore we decrement the index as long as there are bytes matching this pattern. + // This ensures we cut the string at the border between one character and another. + while index > 0 && (p[index] & 0b11000000) == 0b10000000 { + index -= 1; + } + &string[0..index] +} + +#[derive(Clone, Copy)] +pub enum Compression { + None, + Gzip, +} + +impl Compression { + pub fn from_env(var_name: &str) -> Compression { + match std::env::var(var_name) { + Ok(method) => match Compression::from_string(&method) { + Some(c) => c, + None => { + tracing::error!("Unknown compression method '{}'; using gzip.", &method); + Compression::Gzip + } + }, + // Default compression method if the env var isn't set: + Err(_) => Compression::Gzip, + } + } + + pub fn from_string(s: &str) -> Option { + match s.to_lowercase().as_ref() { + "none" => Some(Compression::None), + "gzip" => Some(Compression::Gzip), + _ => None, + } + } + + pub fn extension(&self) -> &str { + match self { + Compression::None => "trap", + Compression::Gzip => "trap.gz", + } + } +} + +#[test] +fn limit_string_test() { + assert_eq!("hello", limit_string(&"hello world".to_owned(), 5)); + assert_eq!("hi ☹", limit_string(&"hi ☹☹".to_owned(), 6)); + assert_eq!("hi ", limit_string(&"hi ☹☹".to_owned(), 5)); +} + +#[test] +fn escape_key_test() { + assert_eq!("foo!", escape_key("foo!")); + assert_eq!("foo{}", escape_key("foo{}")); + assert_eq!("{}", escape_key("{}")); + assert_eq!("", escape_key("")); + assert_eq!("/path/to/foo.rb", escape_key("/path/to/foo.rb")); + assert_eq!( + "/path/to/foo&{}"@#.rb", + escape_key("/path/to/foo&{}\"@#.rb") + ); +} From cc958dc1711ef9b3943bb8272f0b18334ea4ca64 Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Thu, 21 Jul 2022 17:19:33 -0400 Subject: [PATCH 312/505] Update ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql Co-authored-by: Harry Maclean --- .../manually-check-http-verb/ManuallyCheckHttpVerb.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql b/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql index 354ccf62698..2ddf7fe87b3 100644 --- a/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql +++ b/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql @@ -91,6 +91,6 @@ class HttpVerbConfig extends TaintTracking::Configuration { } from HttpVerbConfig config, DataFlow::PathNode source, DataFlow::PathNode sink -where config.hasFlow(source.getNode(), sink.getNode()) +where config.hasFlowPath(source, sink) select sink.getNode(), source, sink, "Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods." From 8fabc06d370e88603e809d35e76bd3c13bed8495 Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Thu, 21 Jul 2022 21:25:44 +0000 Subject: [PATCH 313/505] fix test assertion --- .../ManuallyCheckHttpVerb.expected | 9 --------- 1 file changed, 9 deletions(-) diff --git a/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.expected b/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.expected index a253c3e9313..1fdf8045c23 100644 --- a/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.expected +++ b/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.expected @@ -24,18 +24,9 @@ nodes subpaths #select | ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? | ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? | ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | -| ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? | ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? | ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? : | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | -| ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? | ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? : | ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | -| ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? | ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? : | ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? : | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | | ManuallyCheckHttpVerb.rb:12:8:12:22 | ... == ... | ManuallyCheckHttpVerb.rb:11:14:11:24 | call to env : | ManuallyCheckHttpVerb.rb:12:8:12:22 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | -| ManuallyCheckHttpVerb.rb:12:8:12:22 | ... == ... | ManuallyCheckHttpVerb.rb:11:14:11:24 | call to env : | ManuallyCheckHttpVerb.rb:12:8:12:22 | ... == ... : | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | | ManuallyCheckHttpVerb.rb:20:8:20:22 | ... == ... | ManuallyCheckHttpVerb.rb:19:14:19:35 | call to request_method : | ManuallyCheckHttpVerb.rb:20:8:20:22 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | -| ManuallyCheckHttpVerb.rb:20:8:20:22 | ... == ... | ManuallyCheckHttpVerb.rb:19:14:19:35 | call to request_method : | ManuallyCheckHttpVerb.rb:20:8:20:22 | ... == ... : | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | | ManuallyCheckHttpVerb.rb:28:8:28:22 | ... == ... | ManuallyCheckHttpVerb.rb:27:14:27:27 | call to method : | ManuallyCheckHttpVerb.rb:28:8:28:22 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | -| ManuallyCheckHttpVerb.rb:28:8:28:22 | ... == ... | ManuallyCheckHttpVerb.rb:27:14:27:27 | call to method : | ManuallyCheckHttpVerb.rb:28:8:28:22 | ... == ... : | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | | ManuallyCheckHttpVerb.rb:36:8:36:22 | ... == ... | ManuallyCheckHttpVerb.rb:35:14:35:39 | call to raw_request_method : | ManuallyCheckHttpVerb.rb:36:8:36:22 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | -| ManuallyCheckHttpVerb.rb:36:8:36:22 | ... == ... | ManuallyCheckHttpVerb.rb:35:14:35:39 | call to raw_request_method : | ManuallyCheckHttpVerb.rb:36:8:36:22 | ... == ... : | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | | ManuallyCheckHttpVerb.rb:52:10:52:23 | ... == ... | ManuallyCheckHttpVerb.rb:51:16:51:44 | call to request_method_symbol : | ManuallyCheckHttpVerb.rb:52:10:52:23 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | -| ManuallyCheckHttpVerb.rb:52:10:52:23 | ... == ... | ManuallyCheckHttpVerb.rb:51:16:51:44 | call to request_method_symbol : | ManuallyCheckHttpVerb.rb:52:10:52:23 | ... == ... : | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | | ManuallyCheckHttpVerb.rb:59:10:59:38 | ...[...] | ManuallyCheckHttpVerb.rb:59:10:59:20 | call to env : | ManuallyCheckHttpVerb.rb:59:10:59:38 | ...[...] | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | -| ManuallyCheckHttpVerb.rb:59:10:59:38 | ...[...] | ManuallyCheckHttpVerb.rb:59:10:59:20 | call to env : | ManuallyCheckHttpVerb.rb:59:10:59:38 | ...[...] : | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | From 486a394a7f1979a69f6a9b82f804645111b05876 Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Thu, 21 Jul 2022 17:26:09 -0400 Subject: [PATCH 314/505] Update ruby/ql/src/experimental/weak-params/WeakParams.ql Co-authored-by: Harry Maclean --- ruby/ql/src/experimental/weak-params/WeakParams.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruby/ql/src/experimental/weak-params/WeakParams.ql b/ruby/ql/src/experimental/weak-params/WeakParams.ql index 0f36d4930d3..8789018e2cc 100644 --- a/ruby/ql/src/experimental/weak-params/WeakParams.ql +++ b/ruby/ql/src/experimental/weak-params/WeakParams.ql @@ -39,7 +39,7 @@ class WeakParams extends DataFlow::CallNode { WeakParams() { this.getReceiver() instanceof ActionControllerRequest and this.getMethodName() = - ["path_parametes", "query_parameters", "request_parameters", "GET", "POST"] + ["path_parameters", "query_parameters", "request_parameters", "GET", "POST"] } } From c1a6ca5f9458346453a98938b1e0616e8428f43f Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Thu, 21 Jul 2022 22:11:14 +0000 Subject: [PATCH 315/505] add change note --- ruby/ql/src/change-notes/2022-07-21-check-http-verb.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 ruby/ql/src/change-notes/2022-07-21-check-http-verb.md diff --git a/ruby/ql/src/change-notes/2022-07-21-check-http-verb.md b/ruby/ql/src/change-notes/2022-07-21-check-http-verb.md new file mode 100644 index 00000000000..3d1a978953d --- /dev/null +++ b/ruby/ql/src/change-notes/2022-07-21-check-http-verb.md @@ -0,0 +1,4 @@ +--- +category: newQuery +--- +* Added a new query, rb/manually-checking-http-verb, to detect cases when the HTTP verb for an incoming request is checked and then used as part of control flow. \ No newline at end of file From 1842bde879caddebd17a3498eca3e5b33f70107d Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Thu, 21 Jul 2022 22:13:53 +0000 Subject: [PATCH 316/505] add change note --- ruby/ql/src/change-notes/2022-07-21-weak-params.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 ruby/ql/src/change-notes/2022-07-21-weak-params.md diff --git a/ruby/ql/src/change-notes/2022-07-21-weak-params.md b/ruby/ql/src/change-notes/2022-07-21-weak-params.md new file mode 100644 index 00000000000..c00926492af --- /dev/null +++ b/ruby/ql/src/change-notes/2022-07-21-weak-params.md @@ -0,0 +1,4 @@ +--- +category: newQuery +--- +* Added a new query, rb/weak-params, to detect cases when the rails strong parameters pattern isn't followed and the values flow into persistent store writes \ No newline at end of file From 7e67338fb5bdf10da2b088227d7c241e774d8a8e Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Fri, 22 Jul 2022 13:34:11 +0200 Subject: [PATCH 317/505] Update swift/extractor/infra/SwiftDispatcher.h Co-authored-by: Jeroen Ketema <93738568+jketema@users.noreply.github.com> --- swift/extractor/infra/SwiftDispatcher.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swift/extractor/infra/SwiftDispatcher.h b/swift/extractor/infra/SwiftDispatcher.h index d3490b56b9f..4b604f4e00a 100644 --- a/swift/extractor/infra/SwiftDispatcher.h +++ b/swift/extractor/infra/SwiftDispatcher.h @@ -265,7 +265,7 @@ class SwiftDispatcher { bool fetchLabelFromUnionCase(const llvm::PointerUnion u, TrapLabel& output) { // we rely on the fact that when we extract `ASTNode` instances (which only happens // on `BraceStmt` elements), we cannot encounter a standalone `TypeRepr` there, so we skip - // this case, which would be problematic as we would not be able to provide the corresponding + // this case; extracting `TypeRepr`s here would be problematic as we would not be able to provide the corresponding // type if constexpr (!std::is_same_v) { if (auto e = u.template dyn_cast()) { From d44bf326f0db8e26df38c7f63024f4fb056d8720 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Fri, 22 Jul 2022 13:36:22 +0200 Subject: [PATCH 318/505] Update ruby/extractor/src/main.rs Co-authored-by: Nick Rolfe --- ruby/extractor/src/main.rs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/ruby/extractor/src/main.rs b/ruby/extractor/src/main.rs index a3ed7ed7933..ca1039e4e0d 100644 --- a/ruby/extractor/src/main.rs +++ b/ruby/extractor/src/main.rs @@ -48,17 +48,13 @@ lazy_static! { } fn encoding_from_name(encoding_name: &str) -> Option<&(dyn encoding::Encoding + Send + Sync)> { - match encoding::label::encoding_from_whatwg_label(&encoding_name) { - Some(e) => return Some(e), - None => { - if let Some(cap) = CP_NUMBER.captures(&encoding_name) { - return encoding::label::encoding_from_windows_code_page( - str::parse(cap.get(1).unwrap().as_str()).unwrap(), - ); - } else { - return None; - } - } + match encoding::label::encoding_from_whatwg_label(encoding_name) { + s @ Some(_) => s, + None => CP_NUMBER.captures(encoding_name).and_then(|cap| { + encoding::label::encoding_from_windows_code_page( + str::parse(cap.get(1).unwrap().as_str()).unwrap(), + ) + }), } } From 77401ded4ec353c556e2f25a124fae42aebab3f7 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Fri, 22 Jul 2022 13:54:32 +0200 Subject: [PATCH 319/505] Swift: reflow comment --- swift/extractor/infra/SwiftDispatcher.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/swift/extractor/infra/SwiftDispatcher.h b/swift/extractor/infra/SwiftDispatcher.h index 4b604f4e00a..bfcefb41970 100644 --- a/swift/extractor/infra/SwiftDispatcher.h +++ b/swift/extractor/infra/SwiftDispatcher.h @@ -265,8 +265,8 @@ class SwiftDispatcher { bool fetchLabelFromUnionCase(const llvm::PointerUnion u, TrapLabel& output) { // we rely on the fact that when we extract `ASTNode` instances (which only happens // on `BraceStmt` elements), we cannot encounter a standalone `TypeRepr` there, so we skip - // this case; extracting `TypeRepr`s here would be problematic as we would not be able to provide the corresponding - // type + // this case; extracting `TypeRepr`s here would be problematic as we would not be able to + // provide the corresponding type if constexpr (!std::is_same_v) { if (auto e = u.template dyn_cast()) { output = fetchLabel(e); From 4767d5a1ba544dcd8a60e6372d545930e5096fa1 Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Fri, 22 Jul 2022 15:37:53 +0100 Subject: [PATCH 320/505] Ruby/QL: speed up trap writing by putting BufWriter in front of GzEncoder --- ql/extractor/src/trap.rs | 11 +++++++---- ruby/extractor/src/trap.rs | 11 +++++++---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/ql/extractor/src/trap.rs b/ql/extractor/src/trap.rs index d64c520c4cc..35a9b69f255 100644 --- a/ql/extractor/src/trap.rs +++ b/ql/extractor/src/trap.rs @@ -61,12 +61,15 @@ impl Writer { pub fn write_to_file(&self, path: &Path, compression: Compression) -> std::io::Result<()> { let trap_file = std::fs::File::create(path)?; - let mut trap_file = BufWriter::new(trap_file); match compression { - Compression::None => self.write_trap_entries(&mut trap_file), + Compression::None => { + let mut trap_file = BufWriter::new(trap_file); + self.write_trap_entries(&mut trap_file) + } Compression::Gzip => { - let mut compressed_writer = GzEncoder::new(trap_file, flate2::Compression::fast()); - self.write_trap_entries(&mut compressed_writer) + let trap_file = GzEncoder::new(trap_file, flate2::Compression::fast()); + let mut trap_file = BufWriter::new(trap_file); + self.write_trap_entries(&mut trap_file) } } } diff --git a/ruby/extractor/src/trap.rs b/ruby/extractor/src/trap.rs index d64c520c4cc..35a9b69f255 100644 --- a/ruby/extractor/src/trap.rs +++ b/ruby/extractor/src/trap.rs @@ -61,12 +61,15 @@ impl Writer { pub fn write_to_file(&self, path: &Path, compression: Compression) -> std::io::Result<()> { let trap_file = std::fs::File::create(path)?; - let mut trap_file = BufWriter::new(trap_file); match compression { - Compression::None => self.write_trap_entries(&mut trap_file), + Compression::None => { + let mut trap_file = BufWriter::new(trap_file); + self.write_trap_entries(&mut trap_file) + } Compression::Gzip => { - let mut compressed_writer = GzEncoder::new(trap_file, flate2::Compression::fast()); - self.write_trap_entries(&mut compressed_writer) + let trap_file = GzEncoder::new(trap_file, flate2::Compression::fast()); + let mut trap_file = BufWriter::new(trap_file); + self.write_trap_entries(&mut trap_file) } } } From a9d95a94188e0f8e626f47e5cc2c63e5cd55b779 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Tue, 28 Jun 2022 16:37:11 +0200 Subject: [PATCH 321/505] C++: Remove `pragma[noinline]` from `ResolveGlobalVariable.ql` --- cpp/ql/lib/semmle/code/cpp/internal/ResolveGlobalVariable.qll | 3 --- 1 file changed, 3 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/internal/ResolveGlobalVariable.qll b/cpp/ql/lib/semmle/code/cpp/internal/ResolveGlobalVariable.qll index 219d45e2991..c11e1457dae 100644 --- a/cpp/ql/lib/semmle/code/cpp/internal/ResolveGlobalVariable.qll +++ b/cpp/ql/lib/semmle/code/cpp/internal/ResolveGlobalVariable.qll @@ -2,13 +2,11 @@ private predicate hasDefinition(@globalvariable g) { exists(@var_decl vd | var_decls(vd, g, _, _, _) | var_def(vd)) } -pragma[noinline] private predicate onlyOneCompleteGlobalVariableExistsWithMangledName(@mangledname name) { strictcount(@globalvariable g | hasDefinition(g) and mangled_name(g, name)) = 1 } /** Holds if `g` is a unique global variable with a definition named `name`. */ -pragma[noinline] private predicate isGlobalWithMangledNameAndWithDefinition(@mangledname name, @globalvariable g) { hasDefinition(g) and mangled_name(g, name) and @@ -16,7 +14,6 @@ private predicate isGlobalWithMangledNameAndWithDefinition(@mangledname name, @g } /** Holds if `g` is a global variable without a definition named `name`. */ -pragma[noinline] private predicate isGlobalWithMangledNameAndWithoutDefinition(@mangledname name, @globalvariable g) { not hasDefinition(g) and mangled_name(g, name) From 2c095cf16631aa5f93ec1b4eb035920a665efab5 Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Fri, 22 Jul 2022 13:51:38 -0400 Subject: [PATCH 322/505] Update ruby/ql/src/change-notes/2022-07-21-weak-params.md Co-authored-by: Harry Maclean --- ruby/ql/src/change-notes/2022-07-21-weak-params.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruby/ql/src/change-notes/2022-07-21-weak-params.md b/ruby/ql/src/change-notes/2022-07-21-weak-params.md index c00926492af..08b8f153989 100644 --- a/ruby/ql/src/change-notes/2022-07-21-weak-params.md +++ b/ruby/ql/src/change-notes/2022-07-21-weak-params.md @@ -1,4 +1,4 @@ --- category: newQuery --- -* Added a new query, rb/weak-params, to detect cases when the rails strong parameters pattern isn't followed and the values flow into persistent store writes \ No newline at end of file +* Added a new experimental query, `rb/weak-params`, to detect cases when the rails strong parameters pattern isn't followed and values flow into persistent store writes. \ No newline at end of file From c2710fb038003a1c29b28ba941ff0367cf42f39c Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Fri, 22 Jul 2022 13:52:00 -0400 Subject: [PATCH 323/505] Update ruby/ql/src/change-notes/2022-07-21-check-http-verb.md Co-authored-by: Harry Maclean --- ruby/ql/src/change-notes/2022-07-21-check-http-verb.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruby/ql/src/change-notes/2022-07-21-check-http-verb.md b/ruby/ql/src/change-notes/2022-07-21-check-http-verb.md index 3d1a978953d..4a670ba1092 100644 --- a/ruby/ql/src/change-notes/2022-07-21-check-http-verb.md +++ b/ruby/ql/src/change-notes/2022-07-21-check-http-verb.md @@ -1,4 +1,4 @@ --- category: newQuery --- -* Added a new query, rb/manually-checking-http-verb, to detect cases when the HTTP verb for an incoming request is checked and then used as part of control flow. \ No newline at end of file +* Added a new experimental query, `rb/manually-checking-http-verb`, to detect cases when the HTTP verb for an incoming request is checked and then used as part of control flow. \ No newline at end of file From f39ca1aad227487a6f1f2d107e634d3515c75650 Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Fri, 22 Jul 2022 18:36:25 +0000 Subject: [PATCH 324/505] correct cwe tagged --- ruby/ql/src/experimental/weak-params/WeakParams.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruby/ql/src/experimental/weak-params/WeakParams.ql b/ruby/ql/src/experimental/weak-params/WeakParams.ql index 8789018e2cc..e2a441fc7c9 100644 --- a/ruby/ql/src/experimental/weak-params/WeakParams.ql +++ b/ruby/ql/src/experimental/weak-params/WeakParams.ql @@ -7,7 +7,7 @@ * @precision medium * @id rb/weak-params * @tags security - * external/cwe/cwe-223 + * external/cwe/cwe-352 */ import ruby From 0c0ba925a7a28d6e76677fc76b915238300d4820 Mon Sep 17 00:00:00 2001 From: thiggy1342 Date: Fri, 22 Jul 2022 18:44:03 +0000 Subject: [PATCH 325/505] this one should have no tag --- ruby/ql/src/experimental/weak-params/WeakParams.ql | 1 - 1 file changed, 1 deletion(-) diff --git a/ruby/ql/src/experimental/weak-params/WeakParams.ql b/ruby/ql/src/experimental/weak-params/WeakParams.ql index e2a441fc7c9..0c6d9db5644 100644 --- a/ruby/ql/src/experimental/weak-params/WeakParams.ql +++ b/ruby/ql/src/experimental/weak-params/WeakParams.ql @@ -7,7 +7,6 @@ * @precision medium * @id rb/weak-params * @tags security - * external/cwe/cwe-352 */ import ruby From 715b0b3fb89030d83a1e18ec1e6c144727281dfe Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Mon, 25 Jul 2022 15:17:14 +0100 Subject: [PATCH 326/505] Accept test changes --- java/ql/test/kotlin/library-tests/reflection/PrintAst.expected | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/test/kotlin/library-tests/reflection/PrintAst.expected b/java/ql/test/kotlin/library-tests/reflection/PrintAst.expected index dd47be234c3..b1da7cdcee2 100644 --- a/java/ql/test/kotlin/library-tests/reflection/PrintAst.expected +++ b/java/ql/test/kotlin/library-tests/reflection/PrintAst.expected @@ -8,7 +8,7 @@ reflection.kt: # 46| 0: [TypeAccess] String # 47| 5: [BlockStmt] { ... } # 47| 0: [ReturnStmt] return ... -# 47| 0: [MethodAccess] get(...) +# 47| 0: [MethodAccess] charAt(...) # 47| -1: [ExtensionReceiverAccess] this # 47| 0: [SubExpr] ... - ... # 47| 0: [MethodAccess] length(...) From 95db81658b4aa3176b5edb3946a76788fdaee1ac Mon Sep 17 00:00:00 2001 From: Tony Torralba Date: Tue, 26 Jul 2022 10:42:24 +0200 Subject: [PATCH 327/505] Add CSV models for java.util.Scanner --- .../java/dataflow/internal/ContainerFlow.qll | 6 + java/ql/test/library-tests/scanner/Test.java | 183 ++++++++++++++++++ .../test/library-tests/scanner/test.expected | 0 java/ql/test/library-tests/scanner/test.ql | 2 + 4 files changed, 191 insertions(+) create mode 100644 java/ql/test/library-tests/scanner/Test.java create mode 100644 java/ql/test/library-tests/scanner/test.expected create mode 100644 java/ql/test/library-tests/scanner/test.ql diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/ContainerFlow.qll b/java/ql/lib/semmle/code/java/dataflow/internal/ContainerFlow.qll index e9c457fc801..3acb3291911 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/ContainerFlow.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/ContainerFlow.qll @@ -244,8 +244,14 @@ private class ContainerFlowSummaries extends SummaryModelCsv { "java.util;Properties;true;getProperty;(String);;Argument[-1].MapValue;ReturnValue;value;manual", "java.util;Properties;true;getProperty;(String,String);;Argument[-1].MapValue;ReturnValue;value;manual", "java.util;Properties;true;getProperty;(String,String);;Argument[1];ReturnValue;value;manual", + "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual", "java.util;Scanner;true;next;(Pattern);;Argument[-1];ReturnValue;taint;manual", "java.util;Scanner;true;next;(String);;Argument[-1];ReturnValue;taint;manual", + "java.util;Scanner;true;reset;;;Argument[-1];ReturnValue;value;manual", + "java.util;Scanner;true;skip;;;Argument[-1];ReturnValue;value;manual", + "java.util;Scanner;true;useDelimiter;;;Argument[-1];ReturnValue;value;manual", + "java.util;Scanner;true;useLocale;;;Argument[-1];ReturnValue;value;manual", + "java.util;Scanner;true;useRadix;;;Argument[-1];ReturnValue;value;manual", "java.util;SortedMap;true;headMap;(Object);;Argument[-1].MapKey;ReturnValue.MapKey;value;manual", "java.util;SortedMap;true;headMap;(Object);;Argument[-1].MapValue;ReturnValue.MapValue;value;manual", "java.util;SortedMap;true;subMap;(Object,Object);;Argument[-1].MapKey;ReturnValue.MapKey;value;manual", diff --git a/java/ql/test/library-tests/scanner/Test.java b/java/ql/test/library-tests/scanner/Test.java new file mode 100644 index 00000000000..d5bd778fe1b --- /dev/null +++ b/java/ql/test/library-tests/scanner/Test.java @@ -0,0 +1,183 @@ +package generatedtest; + +import java.io.File; +import java.io.InputStream; +import java.nio.channels.ReadableByteChannel; +import java.nio.charset.Charset; +import java.nio.file.Path; +import java.util.Scanner; +import java.util.regex.Pattern; + +// Test case generated by GenerateFlowTestCase.ql +public class Test { + + Object source() { return null; } + void sink(Object o) { } + + public void test() throws Exception { + + { + // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" + Scanner out = null; + File in = (File)source(); + out = new Scanner(in); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" + Scanner out = null; + File in = (File)source(); + out = new Scanner(in, (Charset)null); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" + Scanner out = null; + File in = (File)source(); + out = new Scanner(in, (String)null); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" + Scanner out = null; + InputStream in = (InputStream)source(); + out = new Scanner(in); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" + Scanner out = null; + InputStream in = (InputStream)source(); + out = new Scanner(in, (Charset)null); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" + Scanner out = null; + InputStream in = (InputStream)source(); + out = new Scanner(in, (String)null); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" + Scanner out = null; + Path in = (Path)source(); + out = new Scanner(in); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" + Scanner out = null; + Path in = (Path)source(); + out = new Scanner(in, (Charset)null); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" + Scanner out = null; + Path in = (Path)source(); + out = new Scanner(in, (String)null); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" + Scanner out = null; + Readable in = (Readable)source(); + out = new Scanner(in); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" + Scanner out = null; + ReadableByteChannel in = (ReadableByteChannel)source(); + out = new Scanner(in); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" + Scanner out = null; + ReadableByteChannel in = (ReadableByteChannel)source(); + out = new Scanner(in, (Charset)null); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" + Scanner out = null; + ReadableByteChannel in = (ReadableByteChannel)source(); + out = new Scanner(in, (String)null); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" + Scanner out = null; + String in = (String)source(); + out = new Scanner(in); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;next;(Pattern);;Argument[-1];ReturnValue;taint;manual" + String out = null; + Scanner in = (Scanner)source(); + out = in.next((Pattern)null); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;next;(String);;Argument[-1];ReturnValue;taint;manual" + String out = null; + Scanner in = (Scanner)source(); + out = in.next((String)null); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;reset;;;Argument[-1];ReturnValue;value;manual" + Scanner out = null; + Scanner in = (Scanner)source(); + out = in.reset(); + sink(out); // $ hasValueFlow + } + { + // "java.util;Scanner;true;skip;;;Argument[-1];ReturnValue;value;manual" + Scanner out = null; + Scanner in = (Scanner)source(); + out = in.skip((Pattern)null); + sink(out); // $ hasValueFlow + } + { + // "java.util;Scanner;true;skip;;;Argument[-1];ReturnValue;value;manual" + Scanner out = null; + Scanner in = (Scanner)source(); + out = in.skip((String)null); + sink(out); // $ hasValueFlow + } + { + // "java.util;Scanner;true;useDelimiter;;;Argument[-1];ReturnValue;value;manual" + Scanner out = null; + Scanner in = (Scanner)source(); + out = in.useDelimiter((Pattern)null); + sink(out); // $ hasValueFlow + } + { + // "java.util;Scanner;true;useDelimiter;;;Argument[-1];ReturnValue;value;manual" + Scanner out = null; + Scanner in = (Scanner)source(); + out = in.useDelimiter((String)null); + sink(out); // $ hasValueFlow + } + { + // "java.util;Scanner;true;useLocale;;;Argument[-1];ReturnValue;value;manual" + Scanner out = null; + Scanner in = (Scanner)source(); + out = in.useLocale(null); + sink(out); // $ hasValueFlow + } + { + // "java.util;Scanner;true;useRadix;;;Argument[-1];ReturnValue;value;manual" + Scanner out = null; + Scanner in = (Scanner)source(); + out = in.useRadix(0); + sink(out); // $ hasValueFlow + } + + } + +} \ No newline at end of file diff --git a/java/ql/test/library-tests/scanner/test.expected b/java/ql/test/library-tests/scanner/test.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/java/ql/test/library-tests/scanner/test.ql b/java/ql/test/library-tests/scanner/test.ql new file mode 100644 index 00000000000..5d91e4e8e26 --- /dev/null +++ b/java/ql/test/library-tests/scanner/test.ql @@ -0,0 +1,2 @@ +import java +import TestUtilities.InlineFlowTest From c56e0f7c0d55e5e04cd87cd4eb56c2118db15693 Mon Sep 17 00:00:00 2001 From: Tony Torralba Date: Tue, 26 Jul 2022 10:50:34 +0200 Subject: [PATCH 328/505] Add change note --- java/ql/lib/change-notes/2022-07-26-scanner-models.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 java/ql/lib/change-notes/2022-07-26-scanner-models.md diff --git a/java/ql/lib/change-notes/2022-07-26-scanner-models.md b/java/ql/lib/change-notes/2022-07-26-scanner-models.md new file mode 100644 index 00000000000..6a78982d639 --- /dev/null +++ b/java/ql/lib/change-notes/2022-07-26-scanner-models.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Added data flow models for `java.util.Scanner`. \ No newline at end of file From 33f5620782479ea2f449e44faa8857e6eaa59bc5 Mon Sep 17 00:00:00 2001 From: Tony Torralba Date: Tue, 26 Jul 2022 11:06:11 +0200 Subject: [PATCH 329/505] Add more models --- .../java/dataflow/internal/ContainerFlow.qll | 16 +- java/ql/test/library-tests/scanner/Test.java | 229 ++++++++++++++---- 2 files changed, 201 insertions(+), 44 deletions(-) diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/ContainerFlow.qll b/java/ql/lib/semmle/code/java/dataflow/internal/ContainerFlow.qll index 3acb3291911..6c4a369527c 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/ContainerFlow.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/ContainerFlow.qll @@ -245,8 +245,20 @@ private class ContainerFlowSummaries extends SummaryModelCsv { "java.util;Properties;true;getProperty;(String,String);;Argument[-1].MapValue;ReturnValue;value;manual", "java.util;Properties;true;getProperty;(String,String);;Argument[1];ReturnValue;value;manual", "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual", - "java.util;Scanner;true;next;(Pattern);;Argument[-1];ReturnValue;taint;manual", - "java.util;Scanner;true;next;(String);;Argument[-1];ReturnValue;taint;manual", + "java.util;Scanner;true;findInLine;;;Argument[-1];ReturnValue;taint;manual", + "java.util;Scanner;true;findWithinHorizon;;;Argument[-1];ReturnValue;taint;manual", + "java.util;Scanner;true;findWithinHorizon;;;Argument[-1];ReturnValue;taint;manual", + "java.util;Scanner;true;next;;;Argument[-1];ReturnValue;taint;manual", + "java.util;Scanner;true;nextBigDecimal;;;Argument[-1];ReturnValue;taint;manual", + "java.util;Scanner;true;nextBigInteger;;;Argument[-1];ReturnValue;taint;manual", + "java.util;Scanner;true;nextBoolean;;;Argument[-1];ReturnValue;taint;manual", + "java.util;Scanner;true;nextByte;;;Argument[-1];ReturnValue;taint;manual", + "java.util;Scanner;true;nextDouble;;;Argument[-1];ReturnValue;taint;manual", + "java.util;Scanner;true;nextFloat;;;Argument[-1];ReturnValue;taint;manual", + "java.util;Scanner;true;nextInt;;;Argument[-1];ReturnValue;taint;manual", + "java.util;Scanner;true;nextLine;;;Argument[-1];ReturnValue;taint;manual", + "java.util;Scanner;true;nextLong;;;Argument[-1];ReturnValue;taint;manual", + "java.util;Scanner;true;nextShort;;;Argument[-1];ReturnValue;taint;manual", "java.util;Scanner;true;reset;;;Argument[-1];ReturnValue;value;manual", "java.util;Scanner;true;skip;;;Argument[-1];ReturnValue;value;manual", "java.util;Scanner;true;useDelimiter;;;Argument[-1];ReturnValue;value;manual", diff --git a/java/ql/test/library-tests/scanner/Test.java b/java/ql/test/library-tests/scanner/Test.java index d5bd778fe1b..05f5b497215 100644 --- a/java/ql/test/library-tests/scanner/Test.java +++ b/java/ql/test/library-tests/scanner/Test.java @@ -2,6 +2,8 @@ package generatedtest; import java.io.File; import java.io.InputStream; +import java.math.BigDecimal; +import java.math.BigInteger; import java.nio.channels.ReadableByteChannel; import java.nio.charset.Charset; import java.nio.file.Path; @@ -11,173 +13,316 @@ import java.util.regex.Pattern; // Test case generated by GenerateFlowTestCase.ql public class Test { - Object source() { return null; } - void sink(Object o) { } + Object source() { + return null; + } + + void sink(Object o) {} public void test() throws Exception { { // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" Scanner out = null; - File in = (File)source(); + File in = (File) source(); out = new Scanner(in); sink(out); // $ hasTaintFlow } { // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" Scanner out = null; - File in = (File)source(); - out = new Scanner(in, (Charset)null); + File in = (File) source(); + out = new Scanner(in, (Charset) null); sink(out); // $ hasTaintFlow } { // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" Scanner out = null; - File in = (File)source(); - out = new Scanner(in, (String)null); + File in = (File) source(); + out = new Scanner(in, (String) null); sink(out); // $ hasTaintFlow } { // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" Scanner out = null; - InputStream in = (InputStream)source(); + InputStream in = (InputStream) source(); out = new Scanner(in); sink(out); // $ hasTaintFlow } { // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" Scanner out = null; - InputStream in = (InputStream)source(); - out = new Scanner(in, (Charset)null); + InputStream in = (InputStream) source(); + out = new Scanner(in, (Charset) null); sink(out); // $ hasTaintFlow } { // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" Scanner out = null; - InputStream in = (InputStream)source(); - out = new Scanner(in, (String)null); + InputStream in = (InputStream) source(); + out = new Scanner(in, (String) null); sink(out); // $ hasTaintFlow } { // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" Scanner out = null; - Path in = (Path)source(); + Path in = (Path) source(); out = new Scanner(in); sink(out); // $ hasTaintFlow } { // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" Scanner out = null; - Path in = (Path)source(); - out = new Scanner(in, (Charset)null); + Path in = (Path) source(); + out = new Scanner(in, (Charset) null); sink(out); // $ hasTaintFlow } { // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" Scanner out = null; - Path in = (Path)source(); - out = new Scanner(in, (String)null); + Path in = (Path) source(); + out = new Scanner(in, (String) null); sink(out); // $ hasTaintFlow } { // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" Scanner out = null; - Readable in = (Readable)source(); + Readable in = (Readable) source(); out = new Scanner(in); sink(out); // $ hasTaintFlow } { // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" Scanner out = null; - ReadableByteChannel in = (ReadableByteChannel)source(); + ReadableByteChannel in = (ReadableByteChannel) source(); out = new Scanner(in); sink(out); // $ hasTaintFlow } { // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" Scanner out = null; - ReadableByteChannel in = (ReadableByteChannel)source(); - out = new Scanner(in, (Charset)null); + ReadableByteChannel in = (ReadableByteChannel) source(); + out = new Scanner(in, (Charset) null); sink(out); // $ hasTaintFlow } { // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" Scanner out = null; - ReadableByteChannel in = (ReadableByteChannel)source(); - out = new Scanner(in, (String)null); + ReadableByteChannel in = (ReadableByteChannel) source(); + out = new Scanner(in, (String) null); sink(out); // $ hasTaintFlow } { // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" Scanner out = null; - String in = (String)source(); + String in = (String) source(); out = new Scanner(in); sink(out); // $ hasTaintFlow } { - // "java.util;Scanner;true;next;(Pattern);;Argument[-1];ReturnValue;taint;manual" + // "java.util;Scanner;true;findInLine;;;Argument[-1];ReturnValue;taint;manual" String out = null; - Scanner in = (Scanner)source(); - out = in.next((Pattern)null); + Scanner in = (Scanner) source(); + out = in.findInLine((Pattern) null); sink(out); // $ hasTaintFlow } { - // "java.util;Scanner;true;next;(String);;Argument[-1];ReturnValue;taint;manual" + // "java.util;Scanner;true;findInLine;;;Argument[-1];ReturnValue;taint;manual" String out = null; - Scanner in = (Scanner)source(); - out = in.next((String)null); + Scanner in = (Scanner) source(); + out = in.findInLine((String) null); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;findWithinHorizon;;;Argument[-1];ReturnValue;taint;manual" + String out = null; + Scanner in = (Scanner) source(); + out = in.findWithinHorizon((Pattern) null, 0); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;findWithinHorizon;;;Argument[-1];ReturnValue;taint;manual" + String out = null; + Scanner in = (Scanner) source(); + out = in.findWithinHorizon((String) null, 0); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;next;;;Argument[-1];ReturnValue;taint;manual" + String out = null; + Scanner in = (Scanner) source(); + out = in.next((Pattern) null); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;next;;;Argument[-1];ReturnValue;taint;manual" + String out = null; + Scanner in = (Scanner) source(); + out = in.next((String) null); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;next;;;Argument[-1];ReturnValue;taint;manual" + String out = null; + Scanner in = (Scanner) source(); + out = in.next(); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;nextBigDecimal;;;Argument[-1];ReturnValue;taint;manual" + BigDecimal out = null; + Scanner in = (Scanner) source(); + out = in.nextBigDecimal(); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;nextBigInteger;;;Argument[-1];ReturnValue;taint;manual" + BigInteger out = null; + Scanner in = (Scanner) source(); + out = in.nextBigInteger(); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;nextBigInteger;;;Argument[-1];ReturnValue;taint;manual" + BigInteger out = null; + Scanner in = (Scanner) source(); + out = in.nextBigInteger(0); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;nextBoolean;;;Argument[-1];ReturnValue;taint;manual" + boolean out = false; + Scanner in = (Scanner) source(); + out = in.nextBoolean(); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;nextByte;;;Argument[-1];ReturnValue;taint;manual" + byte out = 0; + Scanner in = (Scanner) source(); + out = in.nextByte(); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;nextByte;;;Argument[-1];ReturnValue;taint;manual" + byte out = 0; + Scanner in = (Scanner) source(); + out = in.nextByte(0); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;nextDouble;;;Argument[-1];ReturnValue;taint;manual" + double out = 0; + Scanner in = (Scanner) source(); + out = in.nextDouble(); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;nextFloat;;;Argument[-1];ReturnValue;taint;manual" + float out = 0; + Scanner in = (Scanner) source(); + out = in.nextFloat(); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;nextInt;;;Argument[-1];ReturnValue;taint;manual" + int out = 0; + Scanner in = (Scanner) source(); + out = in.nextInt(); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;nextInt;;;Argument[-1];ReturnValue;taint;manual" + int out = 0; + Scanner in = (Scanner) source(); + out = in.nextInt(0); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;nextLine;;;Argument[-1];ReturnValue;taint;manual" + String out = null; + Scanner in = (Scanner) source(); + out = in.nextLine(); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;nextLong;;;Argument[-1];ReturnValue;taint;manual" + long out = 0; + Scanner in = (Scanner) source(); + out = in.nextLong(); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;nextLong;;;Argument[-1];ReturnValue;taint;manual" + long out = 0; + Scanner in = (Scanner) source(); + out = in.nextLong(0); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;nextShort;;;Argument[-1];ReturnValue;taint;manual" + short out = 0; + Scanner in = (Scanner) source(); + out = in.nextShort(); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;nextShort;;;Argument[-1];ReturnValue;taint;manual" + short out = 0; + Scanner in = (Scanner) source(); + out = in.nextShort(0); sink(out); // $ hasTaintFlow } { // "java.util;Scanner;true;reset;;;Argument[-1];ReturnValue;value;manual" Scanner out = null; - Scanner in = (Scanner)source(); + Scanner in = (Scanner) source(); out = in.reset(); sink(out); // $ hasValueFlow } { // "java.util;Scanner;true;skip;;;Argument[-1];ReturnValue;value;manual" Scanner out = null; - Scanner in = (Scanner)source(); - out = in.skip((Pattern)null); + Scanner in = (Scanner) source(); + out = in.skip((Pattern) null); sink(out); // $ hasValueFlow } { // "java.util;Scanner;true;skip;;;Argument[-1];ReturnValue;value;manual" Scanner out = null; - Scanner in = (Scanner)source(); - out = in.skip((String)null); + Scanner in = (Scanner) source(); + out = in.skip((String) null); sink(out); // $ hasValueFlow } { // "java.util;Scanner;true;useDelimiter;;;Argument[-1];ReturnValue;value;manual" Scanner out = null; - Scanner in = (Scanner)source(); - out = in.useDelimiter((Pattern)null); + Scanner in = (Scanner) source(); + out = in.useDelimiter((Pattern) null); sink(out); // $ hasValueFlow } { // "java.util;Scanner;true;useDelimiter;;;Argument[-1];ReturnValue;value;manual" Scanner out = null; - Scanner in = (Scanner)source(); - out = in.useDelimiter((String)null); + Scanner in = (Scanner) source(); + out = in.useDelimiter((String) null); sink(out); // $ hasValueFlow } { // "java.util;Scanner;true;useLocale;;;Argument[-1];ReturnValue;value;manual" Scanner out = null; - Scanner in = (Scanner)source(); + Scanner in = (Scanner) source(); out = in.useLocale(null); sink(out); // $ hasValueFlow } { // "java.util;Scanner;true;useRadix;;;Argument[-1];ReturnValue;value;manual" Scanner out = null; - Scanner in = (Scanner)source(); + Scanner in = (Scanner) source(); out = in.useRadix(0); sink(out); // $ hasValueFlow } } -} \ No newline at end of file +} From 5086841b46ca2aa3dd2076b85da4f190661beb5a Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Tue, 26 Jul 2022 17:03:46 +0100 Subject: [PATCH 330/505] Kotlin: implement super-method calls If we only look at the dispatch receiver, these show up like `this` references rather than `super` references, preventing flow through super-calls. The super-interface case requires properly noting that interface methods with a body get a `default` modifier in order to avoid QL discarding the method as a possible callee. --- .../src/main/kotlin/KotlinFileExtractor.kt | 36 ++++++++++++++----- .../super-method-calls/test.expected | 2 ++ .../library-tests/super-method-calls/test.kt | 36 +++++++++++++++++++ .../library-tests/super-method-calls/test.ql | 18 ++++++++++ 4 files changed, 84 insertions(+), 8 deletions(-) create mode 100644 java/ql/test/kotlin/library-tests/super-method-calls/test.expected create mode 100644 java/ql/test/kotlin/library-tests/super-method-calls/test.kt create mode 100644 java/ql/test/kotlin/library-tests/super-method-calls/test.ql diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt index 78a81fb0715..4100594ad35 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt @@ -888,6 +888,10 @@ open class KotlinFileExtractor( if (shortName.nameInDB != shortName.kotlinName) { tw.writeKtFunctionOriginalNames(methodId, shortName.kotlinName) } + + if (f.hasInterfaceParent() && f.body != null) { + addModifiers(id, "default") // The actual output class file may or may not have this modifier, depending on the -Xjvm-default setting. + } } tw.writeHasLocation(id, locId) @@ -1386,7 +1390,8 @@ open class KotlinFileExtractor( dispatchReceiver: IrExpression?, extensionReceiver: IrExpression?, typeArguments: List = listOf(), - extractClassTypeArguments: Boolean = false) { + extractClassTypeArguments: Boolean = false, + superQualifierSymbol: IrClassSymbol? = null) { val locId = tw.getLocation(callsite) @@ -1404,7 +1409,8 @@ open class KotlinFileExtractor( dispatchReceiver?.let { { callId -> extractExpressionExpr(dispatchReceiver, enclosingCallable, callId, -1, enclosingStmt) } }, extensionReceiver?.let { { argParent -> extractExpressionExpr(extensionReceiver, enclosingCallable, argParent, 0, enclosingStmt) } }, typeArguments, - extractClassTypeArguments + extractClassTypeArguments, + superQualifierSymbol ) } @@ -1424,7 +1430,8 @@ open class KotlinFileExtractor( extractDispatchReceiver: ((Label) -> Unit)?, extractExtensionReceiver: ((Label) -> Unit)?, typeArguments: List = listOf(), - extractClassTypeArguments: Boolean = false) { + extractClassTypeArguments: Boolean = false, + superQualifierSymbol: IrClassSymbol? = null) { val callTarget = syntacticCallTarget.target.realOverrideTarget val id = tw.getFreshIdLabel() @@ -1483,6 +1490,8 @@ open class KotlinFileExtractor( if (callTarget.shouldExtractAsStatic) { extractStaticTypeAccessQualifier(callTarget, id, locId, enclosingCallable, enclosingStmt) + } else if (superQualifierSymbol != null) { + extractSuperAccess(superQualifierSymbol.typeWith(), enclosingCallable, id, -1, enclosingStmt, locId) } else if (extractDispatchReceiver != null) { extractDispatchReceiver(id) } @@ -1744,7 +1753,7 @@ open class KotlinFileExtractor( else listOf() - extractRawMethodAccess(syntacticCallTarget, c, callable, parent, idx, enclosingStmt, (0 until c.valueArgumentsCount).map { c.getValueArgument(it) }, c.dispatchReceiver, c.extensionReceiver, typeArgs, extractClassTypeArguments) + extractRawMethodAccess(syntacticCallTarget, c, callable, parent, idx, enclosingStmt, (0 until c.valueArgumentsCount).map { c.getValueArgument(it) }, c.dispatchReceiver, c.extensionReceiver, typeArgs, extractClassTypeArguments, c.superQualifierSymbol) } fun extractSpecialEnumFunction(fnName: String){ @@ -3066,6 +3075,17 @@ open class KotlinFileExtractor( } } + private fun extractSuperAccess(irType: IrType, callable: Label, parent: Label, idx: Int, enclosingStmt: Label, locId: Label) = + tw.getFreshIdLabel().also { + val type = useType(irType) + tw.writeExprs_superaccess(it, type.javaResult.id, parent, idx) + tw.writeExprsKotlinType(it, type.kotlinResult.id) + tw.writeHasLocation(it, locId) + tw.writeCallableEnclosingExpr(it, callable) + tw.writeStatementEnclosingExpr(it, enclosingStmt) + extractTypeAccessRecursive(irType, locId, it, 0) + } + private fun extractThisAccess(e: IrGetValue, exprParent: ExprParent, callable: Label) { val containingDeclaration = declarationStack.peek() val locId = tw.getLocation(e) @@ -4020,7 +4040,7 @@ open class KotlinFileExtractor( /** * Extracts a single wildcard type access expression with no enclosing callable and statement. */ - private fun extractWildcardTypeAccess(type: TypeResults, location: Label, parent: Label, idx: Int): Label { + private fun extractWildcardTypeAccess(type: TypeResults, location: Label, parent: Label, idx: Int): Label { val id = tw.getFreshIdLabel() tw.writeExprs_wildcardtypeaccess(id, type.javaResult.id, parent, idx) tw.writeExprsKotlinType(id, type.kotlinResult.id) @@ -4031,7 +4051,7 @@ open class KotlinFileExtractor( /** * Extracts a single type access expression with no enclosing callable and statement. */ - private fun extractTypeAccess(type: TypeResults, location: Label, parent: Label, idx: Int): Label { + private fun extractTypeAccess(type: TypeResults, location: Label, parent: Label, idx: Int): Label { // TODO: elementForLocation allows us to give some sort of // location, but a proper location for the type access will // require upstream changes @@ -4057,7 +4077,7 @@ open class KotlinFileExtractor( * `extractTypeAccessRecursive` if the argument is invariant. * No enclosing callable and statement is extracted, this is useful for type access extraction in field declarations. */ - private fun extractWildcardTypeAccessRecursive(t: IrTypeArgument, location: Label, parent: Label, idx: Int) { + private fun extractWildcardTypeAccessRecursive(t: IrTypeArgument, location: Label, parent: Label, idx: Int) { val typeLabels by lazy { TypeResults(getTypeArgumentLabel(t), TypeResult(fakeKotlinType(), "TODO", "TODO")) } when (t) { is IrStarProjection -> extractWildcardTypeAccess(typeLabels, location, parent, idx) @@ -4077,7 +4097,7 @@ open class KotlinFileExtractor( * Extracts a type access expression and its child type access expressions in case of a generic type. Nested generics are also handled. * No enclosing callable and statement is extracted, this is useful for type access extraction in field declarations. */ - private fun extractTypeAccessRecursive(t: IrType, location: Label, parent: Label, idx: Int, typeContext: TypeContext = TypeContext.OTHER): Label { + private fun extractTypeAccessRecursive(t: IrType, location: Label, parent: Label, idx: Int, typeContext: TypeContext = TypeContext.OTHER): Label { val typeAccessId = extractTypeAccess(useType(t, typeContext), location, parent, idx) if (t is IrSimpleType) { t.arguments.forEachIndexed { argIdx, arg -> diff --git a/java/ql/test/kotlin/library-tests/super-method-calls/test.expected b/java/ql/test/kotlin/library-tests/super-method-calls/test.expected new file mode 100644 index 00000000000..841eb6298c7 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/super-method-calls/test.expected @@ -0,0 +1,2 @@ +| test.kt:31:17:31:24 | source(...) | test.kt:31:15:31:25 | f(...) | +| test.kt:32:17:32:24 | source(...) | test.kt:32:15:32:25 | g(...) | diff --git a/java/ql/test/kotlin/library-tests/super-method-calls/test.kt b/java/ql/test/kotlin/library-tests/super-method-calls/test.kt new file mode 100644 index 00000000000..7b3e90d7359 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/super-method-calls/test.kt @@ -0,0 +1,36 @@ +open class A { + + open fun f(x: String) = x + +} + +interface B { + + fun g(x: String) = x + +} + +interface C { + + fun g(x: String) = x + +} + +class User : A(), B, C { + + override fun f(x: String) = super.f(x) + + override fun g(x: String) = super.g(x) + + fun source() = "tainted" + + fun sink(s: String) { } + + fun test() { + + sink(this.f(source())) + sink(this.g(source())) + + } + +} diff --git a/java/ql/test/kotlin/library-tests/super-method-calls/test.ql b/java/ql/test/kotlin/library-tests/super-method-calls/test.ql new file mode 100644 index 00000000000..9a628624c91 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/super-method-calls/test.ql @@ -0,0 +1,18 @@ +import java +import semmle.code.java.dataflow.DataFlow + +class Config extends DataFlow::Configuration { + Config() { this = "abc" } + + override predicate isSource(DataFlow::Node n) { + n.asExpr().(MethodAccess).getMethod().getName() = "source" + } + + override predicate isSink(DataFlow::Node n) { + n.asExpr().(Argument).getCall().getCallee().getName() = "sink" + } +} + +from Config c, DataFlow::Node n1, DataFlow::Node n2 +where c.hasFlow(n1, n2) +select n1, n2 From 30accecd8a4db51a368bb4870e16989ba8c5c8e9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 27 Jul 2022 00:19:16 +0000 Subject: [PATCH 331/505] Add changed framework coverage reports --- java/documentation/library-coverage/coverage.csv | 2 +- java/documentation/library-coverage/coverage.rst | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/java/documentation/library-coverage/coverage.csv b/java/documentation/library-coverage/coverage.csv index ce777afae1d..598035e03c3 100644 --- a/java/documentation/library-coverage/coverage.csv +++ b/java/documentation/library-coverage/coverage.csv @@ -36,7 +36,7 @@ java.lang,13,,58,,,,,,,,,,,8,,,,,4,,,1,,,,,,,,,,,,,,,46,12 java.net,10,3,7,,,,,,,,,,,,,,10,,,,,,,,,,,,,,,,,,,3,7, java.nio,15,,6,,13,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,6, java.sql,11,,,,,,,,,4,,,,,,,,,,,,,,,,7,,,,,,,,,,,, -java.util,44,,441,,,,,,,,,,,34,,,,,,5,2,,1,2,,,,,,,,,,,,,24,417 +java.util,44,,458,,,,,,,,,,,34,,,,,,5,2,,1,2,,,,,,,,,,,,,36,422 javax.faces.context,2,7,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,7,, javax.jms,,9,57,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,9,57, javax.json,,,123,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,100,23 diff --git a/java/documentation/library-coverage/coverage.rst b/java/documentation/library-coverage/coverage.rst index 3e4ea1201bf..06101b6633d 100644 --- a/java/documentation/library-coverage/coverage.rst +++ b/java/documentation/library-coverage/coverage.rst @@ -15,9 +15,9 @@ Java framework & library support `Apache HttpComponents `_,"``org.apache.hc.core5.*``, ``org.apache.http``",5,136,28,,,3,,,,25 `Google Guava `_,``com.google.common.*``,,728,39,,6,,,,, `JSON-java `_,``org.json``,,236,,,,,,,, - Java Standard Library,``java.*``,3,552,130,28,,,7,,,10 + Java Standard Library,``java.*``,3,569,130,28,,,7,,,10 Java extensions,"``javax.*``, ``jakarta.*``",63,609,32,,,4,,1,1,2 `Spring `_,``org.springframework.*``,29,476,101,,,,19,14,,29 Others,"``androidx.slice``, ``cn.hutool.core.codec``, ``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.fasterxml.jackson.core``, ``com.fasterxml.jackson.databind``, ``com.opensymphony.xwork2.ognl``, ``com.rabbitmq.client``, ``com.unboundid.ldap.sdk``, ``com.zaxxer.hikari``, ``flexjson``, ``groovy.lang``, ``groovy.util``, ``jodd.json``, ``kotlin.jvm.internal``, ``net.sf.saxon.s9api``, ``ognl``, ``okhttp3``, ``org.apache.commons.codec``, ``org.apache.commons.jexl2``, ``org.apache.commons.jexl3``, ``org.apache.commons.logging``, ``org.apache.commons.ognl``, ``org.apache.directory.ldap.client.api``, ``org.apache.ibatis.jdbc``, ``org.apache.log4j``, ``org.apache.logging.log4j``, ``org.apache.shiro.codec``, ``org.apache.shiro.jndi``, ``org.codehaus.groovy.control``, ``org.dom4j``, ``org.hibernate``, ``org.jboss.logging``, ``org.jdbi.v3.core``, ``org.jooq``, ``org.mvel2``, ``org.scijava.log``, ``org.slf4j``, ``org.xml.sax``, ``org.xmlpull.v1``, ``play.mvc``, ``ratpack.core.form``, ``ratpack.core.handling``, ``ratpack.core.http``, ``ratpack.exec``, ``ratpack.form``, ``ratpack.func``, ``ratpack.handling``, ``ratpack.http``, ``ratpack.util``, ``retrofit2``",65,395,932,,,,14,18,,3 - Totals,,217,6413,1474,117,6,10,107,33,1,84 + Totals,,217,6430,1474,117,6,10,107,33,1,84 From cc423af8f1d28ae3cc03751983f7f9a140cf19aa Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Wed, 27 Jul 2022 10:20:47 +0200 Subject: [PATCH 332/505] Java: Add support for JUnit5 assertions in the nullness queries. --- java/ql/lib/change-notes/2022-07-27-nullness-junit5.md | 5 +++++ java/ql/lib/semmle/code/java/frameworks/Assertions.qll | 9 +++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 java/ql/lib/change-notes/2022-07-27-nullness-junit5.md diff --git a/java/ql/lib/change-notes/2022-07-27-nullness-junit5.md b/java/ql/lib/change-notes/2022-07-27-nullness-junit5.md new file mode 100644 index 00000000000..6cfb0949c69 --- /dev/null +++ b/java/ql/lib/change-notes/2022-07-27-nullness-junit5.md @@ -0,0 +1,5 @@ +--- +category: minorAnalysis +--- +* The JUnit5 version of `AssertNotNull` is now recognized, which removes + related false positives in the nullness queries. diff --git a/java/ql/lib/semmle/code/java/frameworks/Assertions.qll b/java/ql/lib/semmle/code/java/frameworks/Assertions.qll index 3c3d090b993..ff95c71b037 100644 --- a/java/ql/lib/semmle/code/java/frameworks/Assertions.qll +++ b/java/ql/lib/semmle/code/java/frameworks/Assertions.qll @@ -2,7 +2,8 @@ * A library providing uniform access to various assertion frameworks. * * Currently supports `org.junit.Assert`, `junit.framework.*`, - * `com.google.common.base.Preconditions`, and `java.util.Objects`. + * `org.junit.jupiter.api.Assertions`, `com.google.common.base.Preconditions`, + * and `java.util.Objects`. */ import java @@ -17,7 +18,11 @@ private newtype AssertKind = private predicate assertionMethod(Method m, AssertKind kind) { exists(RefType junit | m.getDeclaringType() = junit and - (junit.hasQualifiedName("org.junit", "Assert") or junit.hasQualifiedName("junit.framework", _)) + ( + junit.hasQualifiedName("org.junit", "Assert") or + junit.hasQualifiedName("junit.framework", _) or + junit.hasQualifiedName("org.junit.jupiter.api", "Assertions") + ) | m.hasName("assertNotNull") and kind = AssertKindNotNull() or From ebf650c0c0e4f41c86da4ae9860465b7159706bb Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Wed, 27 Jul 2022 14:56:02 +0200 Subject: [PATCH 333/505] Control Flow: add more ordering for edges --- .../csharp/controlflow/internal/ControlFlowGraphImplShared.qll | 2 +- .../ruby/controlflow/internal/ControlFlowGraphImplShared.qll | 2 +- .../swift/controlflow/internal/ControlFlowGraphImplShared.qll | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll index 47fcd24883c..13fb796ca3d 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll @@ -916,7 +916,7 @@ module TestOutput { s order by l.getFile().getBaseName(), l.getFile().getAbsolutePath(), l.getStartLine(), - l.getStartColumn(), l.getEndLine(), l.getEndColumn(), t.toString() + l.getStartColumn(), l.getEndLine(), l.getEndColumn(), t.toString(), s.toString() ) ).toString() } diff --git a/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplShared.qll b/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplShared.qll index 47fcd24883c..13fb796ca3d 100644 --- a/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplShared.qll +++ b/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplShared.qll @@ -916,7 +916,7 @@ module TestOutput { s order by l.getFile().getBaseName(), l.getFile().getAbsolutePath(), l.getStartLine(), - l.getStartColumn(), l.getEndLine(), l.getEndColumn(), t.toString() + l.getStartColumn(), l.getEndLine(), l.getEndColumn(), t.toString(), s.toString() ) ).toString() } diff --git a/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImplShared.qll b/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImplShared.qll index 47fcd24883c..13fb796ca3d 100644 --- a/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImplShared.qll +++ b/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImplShared.qll @@ -916,7 +916,7 @@ module TestOutput { s order by l.getFile().getBaseName(), l.getFile().getAbsolutePath(), l.getStartLine(), - l.getStartColumn(), l.getEndLine(), l.getEndColumn(), t.toString() + l.getStartColumn(), l.getEndLine(), l.getEndColumn(), t.toString(), s.toString() ) ).toString() } From 7ca955a0e6ca6e4903b76f8eb2fcd7cf15de6dca Mon Sep 17 00:00:00 2001 From: Tony Torralba Date: Wed, 27 Jul 2022 17:23:10 +0200 Subject: [PATCH 334/505] Add support for XML InlineExpectationsTest --- .../InlineExpectationsTestPrivate.qll | 12 ++++++++++++ java/ql/test/library-tests/xml/Test.java | 3 +++ java/ql/test/library-tests/xml/XMLTest.expected | 0 java/ql/test/library-tests/xml/XMLTest.ql | 17 +++++++++++++++++ java/ql/test/library-tests/xml/test.xml | 4 ++++ 5 files changed, 36 insertions(+) create mode 100644 java/ql/test/library-tests/xml/Test.java create mode 100644 java/ql/test/library-tests/xml/XMLTest.expected create mode 100644 java/ql/test/library-tests/xml/XMLTest.ql create mode 100644 java/ql/test/library-tests/xml/test.xml diff --git a/java/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll b/java/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll index 5233f92f406..7e4a1b65f4c 100644 --- a/java/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll +++ b/java/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll @@ -20,3 +20,15 @@ private class KtExpectationComment extends KtComment, ExpectationComment { override string getContents() { result = this.getText().suffix(2).trim() } } + +private class XMLExpectationComment extends ExpectationComment instanceof XMLComment { + override string getContents() { result = this.(XMLComment).getText().trim() } + + override Location getLocation() { result = this.(XMLComment).getLocation() } + + override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { + this.(XMLComment).hasLocationInfo(path, sl, sc, el, ec) + } + + override string toString() { result = this.(XMLComment).toString() } +} diff --git a/java/ql/test/library-tests/xml/Test.java b/java/ql/test/library-tests/xml/Test.java new file mode 100644 index 00000000000..4566fbca2ad --- /dev/null +++ b/java/ql/test/library-tests/xml/Test.java @@ -0,0 +1,3 @@ +public class Test { + +} diff --git a/java/ql/test/library-tests/xml/XMLTest.expected b/java/ql/test/library-tests/xml/XMLTest.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/java/ql/test/library-tests/xml/XMLTest.ql b/java/ql/test/library-tests/xml/XMLTest.ql new file mode 100644 index 00000000000..e4b8b3a0361 --- /dev/null +++ b/java/ql/test/library-tests/xml/XMLTest.ql @@ -0,0 +1,17 @@ +import semmle.code.xml.XML +import TestUtilities.InlineExpectationsTest + +class XMLTest extends InlineExpectationsTest { + XMLTest() { this = "XMLTest" } + + override string getARelevantTag() { result = "hasXmlResult" } + + override predicate hasActualResult(Location location, string element, string tag, string value) { + tag = "hasXmlResult" and + exists(XMLAttribute a | + a.getLocation() = location and + element = a.toString() and + value = "" + ) + } +} diff --git a/java/ql/test/library-tests/xml/test.xml b/java/ql/test/library-tests/xml/test.xml new file mode 100644 index 00000000000..cd41bd7f36c --- /dev/null +++ b/java/ql/test/library-tests/xml/test.xml @@ -0,0 +1,4 @@ + + + Text + \ No newline at end of file From 9e773302ed2539a7cb2126b92038f8f46ce855b4 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 27 Jul 2022 17:30:01 +0100 Subject: [PATCH 335/505] Swift: Extend test cases. --- .../CWE-135/StringLengthConflation.expected | 104 +++++++++--------- .../CWE-135/StringLengthConflation.swift | 17 +-- 2 files changed, 62 insertions(+), 59 deletions(-) diff --git a/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.expected b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.expected index 1af416dad2f..dec1e4799c7 100644 --- a/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.expected +++ b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.expected @@ -1,19 +1,19 @@ edges | StringLengthConflation.swift:60:47:60:50 | .length : | StringLengthConflation.swift:60:47:60:59 | ... call to /(_:_:) ... | | StringLengthConflation.swift:66:33:66:36 | .length : | StringLengthConflation.swift:66:33:66:45 | ... call to /(_:_:) ... | -| StringLengthConflation.swift:93:28:93:31 | .length : | StringLengthConflation.swift:93:28:93:40 | ... call to -(_:_:) ... | -| StringLengthConflation.swift:97:27:97:30 | .length : | StringLengthConflation.swift:97:27:97:39 | ... call to -(_:_:) ... | -| StringLengthConflation.swift:101:25:101:28 | .length : | StringLengthConflation.swift:101:25:101:37 | ... call to -(_:_:) ... | -| StringLengthConflation.swift:105:25:105:28 | .length : | StringLengthConflation.swift:105:25:105:37 | ... call to -(_:_:) ... | -| StringLengthConflation.swift:111:23:111:26 | .length : | StringLengthConflation.swift:111:23:111:35 | ... call to -(_:_:) ... | -| StringLengthConflation.swift:117:22:117:25 | .length : | StringLengthConflation.swift:117:22:117:34 | ... call to -(_:_:) ... | -| StringLengthConflation.swift:122:34:122:36 | .count : | StringLengthConflation.swift:122:34:122:44 | ... call to -(_:_:) ... | -| StringLengthConflation.swift:123:36:123:38 | .count : | StringLengthConflation.swift:123:36:123:46 | ... call to -(_:_:) ... | -| StringLengthConflation.swift:128:36:128:38 | .count : | StringLengthConflation.swift:128:36:128:46 | ... call to -(_:_:) ... | -| StringLengthConflation.swift:129:38:129:40 | .count : | StringLengthConflation.swift:129:38:129:48 | ... call to -(_:_:) ... | -| StringLengthConflation.swift:134:34:134:36 | .count : | StringLengthConflation.swift:134:34:134:44 | ... call to -(_:_:) ... | -| StringLengthConflation.swift:135:36:135:38 | .count : | StringLengthConflation.swift:135:36:135:46 | ... call to -(_:_:) ... | -| StringLengthConflation.swift:141:28:141:30 | .count : | StringLengthConflation.swift:141:28:141:38 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:96:28:96:31 | .length : | StringLengthConflation.swift:96:28:96:40 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:100:27:100:30 | .length : | StringLengthConflation.swift:100:27:100:39 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:104:25:104:28 | .length : | StringLengthConflation.swift:104:25:104:37 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:108:25:108:28 | .length : | StringLengthConflation.swift:108:25:108:37 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:114:23:114:26 | .length : | StringLengthConflation.swift:114:23:114:35 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:120:22:120:25 | .length : | StringLengthConflation.swift:120:22:120:34 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:125:34:125:36 | .count : | StringLengthConflation.swift:125:34:125:44 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:126:36:126:38 | .count : | StringLengthConflation.swift:126:36:126:46 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:131:36:131:38 | .count : | StringLengthConflation.swift:131:36:131:46 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:132:38:132:40 | .count : | StringLengthConflation.swift:132:38:132:48 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:137:34:137:36 | .count : | StringLengthConflation.swift:137:34:137:44 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:138:36:138:38 | .count : | StringLengthConflation.swift:138:36:138:46 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:144:28:144:30 | .count : | StringLengthConflation.swift:144:28:144:38 | ... call to -(_:_:) ... | nodes | StringLengthConflation.swift:53:43:53:46 | .length | semmle.label | .length | | StringLengthConflation.swift:60:47:60:50 | .length : | semmle.label | .length : | @@ -22,32 +22,32 @@ nodes | StringLengthConflation.swift:66:33:66:45 | ... call to /(_:_:) ... | semmle.label | ... call to /(_:_:) ... | | StringLengthConflation.swift:72:33:72:35 | .count | semmle.label | .count | | StringLengthConflation.swift:78:47:78:49 | .count | semmle.label | .count | -| StringLengthConflation.swift:93:28:93:31 | .length : | semmle.label | .length : | -| StringLengthConflation.swift:93:28:93:40 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | -| StringLengthConflation.swift:97:27:97:30 | .length : | semmle.label | .length : | -| StringLengthConflation.swift:97:27:97:39 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | -| StringLengthConflation.swift:101:25:101:28 | .length : | semmle.label | .length : | -| StringLengthConflation.swift:101:25:101:37 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | -| StringLengthConflation.swift:105:25:105:28 | .length : | semmle.label | .length : | -| StringLengthConflation.swift:105:25:105:37 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | -| StringLengthConflation.swift:111:23:111:26 | .length : | semmle.label | .length : | -| StringLengthConflation.swift:111:23:111:35 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | -| StringLengthConflation.swift:117:22:117:25 | .length : | semmle.label | .length : | -| StringLengthConflation.swift:117:22:117:34 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | -| StringLengthConflation.swift:122:34:122:36 | .count : | semmle.label | .count : | -| StringLengthConflation.swift:122:34:122:44 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | -| StringLengthConflation.swift:123:36:123:38 | .count : | semmle.label | .count : | -| StringLengthConflation.swift:123:36:123:46 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | -| StringLengthConflation.swift:128:36:128:38 | .count : | semmle.label | .count : | -| StringLengthConflation.swift:128:36:128:46 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | -| StringLengthConflation.swift:129:38:129:40 | .count : | semmle.label | .count : | -| StringLengthConflation.swift:129:38:129:48 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | -| StringLengthConflation.swift:134:34:134:36 | .count : | semmle.label | .count : | -| StringLengthConflation.swift:134:34:134:44 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | -| StringLengthConflation.swift:135:36:135:38 | .count : | semmle.label | .count : | -| StringLengthConflation.swift:135:36:135:46 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | -| StringLengthConflation.swift:141:28:141:30 | .count : | semmle.label | .count : | -| StringLengthConflation.swift:141:28:141:38 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:96:28:96:31 | .length : | semmle.label | .length : | +| StringLengthConflation.swift:96:28:96:40 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:100:27:100:30 | .length : | semmle.label | .length : | +| StringLengthConflation.swift:100:27:100:39 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:104:25:104:28 | .length : | semmle.label | .length : | +| StringLengthConflation.swift:104:25:104:37 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:108:25:108:28 | .length : | semmle.label | .length : | +| StringLengthConflation.swift:108:25:108:37 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:114:23:114:26 | .length : | semmle.label | .length : | +| StringLengthConflation.swift:114:23:114:35 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:120:22:120:25 | .length : | semmle.label | .length : | +| StringLengthConflation.swift:120:22:120:34 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:125:34:125:36 | .count : | semmle.label | .count : | +| StringLengthConflation.swift:125:34:125:44 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:126:36:126:38 | .count : | semmle.label | .count : | +| StringLengthConflation.swift:126:36:126:46 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:131:36:131:38 | .count : | semmle.label | .count : | +| StringLengthConflation.swift:131:36:131:46 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:132:38:132:40 | .count : | semmle.label | .count : | +| StringLengthConflation.swift:132:38:132:48 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:137:34:137:36 | .count : | semmle.label | .count : | +| StringLengthConflation.swift:137:34:137:44 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:138:36:138:38 | .count : | semmle.label | .count : | +| StringLengthConflation.swift:138:36:138:46 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:144:28:144:30 | .count : | semmle.label | .count : | +| StringLengthConflation.swift:144:28:144:38 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | subpaths #select | StringLengthConflation.swift:53:43:53:46 | .length | StringLengthConflation.swift:53:43:53:46 | .length | StringLengthConflation.swift:53:43:53:46 | .length | This NSString length is used in a String, but it may not be equivalent. | @@ -55,16 +55,16 @@ subpaths | StringLengthConflation.swift:66:33:66:45 | ... call to /(_:_:) ... | StringLengthConflation.swift:66:33:66:36 | .length : | StringLengthConflation.swift:66:33:66:45 | ... call to /(_:_:) ... | This NSString length is used in a String, but it may not be equivalent. | | StringLengthConflation.swift:72:33:72:35 | .count | StringLengthConflation.swift:72:33:72:35 | .count | StringLengthConflation.swift:72:33:72:35 | .count | This String length is used in an NSString, but it may not be equivalent. | | StringLengthConflation.swift:78:47:78:49 | .count | StringLengthConflation.swift:78:47:78:49 | .count | StringLengthConflation.swift:78:47:78:49 | .count | This String length is used in an NSString, but it may not be equivalent. | -| StringLengthConflation.swift:93:28:93:40 | ... call to -(_:_:) ... | StringLengthConflation.swift:93:28:93:31 | .length : | StringLengthConflation.swift:93:28:93:40 | ... call to -(_:_:) ... | This NSString length is used in a String, but it may not be equivalent. | -| StringLengthConflation.swift:97:27:97:39 | ... call to -(_:_:) ... | StringLengthConflation.swift:97:27:97:30 | .length : | StringLengthConflation.swift:97:27:97:39 | ... call to -(_:_:) ... | This NSString length is used in a String, but it may not be equivalent. | -| StringLengthConflation.swift:101:25:101:37 | ... call to -(_:_:) ... | StringLengthConflation.swift:101:25:101:28 | .length : | StringLengthConflation.swift:101:25:101:37 | ... call to -(_:_:) ... | This NSString length is used in a String, but it may not be equivalent. | -| StringLengthConflation.swift:105:25:105:37 | ... call to -(_:_:) ... | StringLengthConflation.swift:105:25:105:28 | .length : | StringLengthConflation.swift:105:25:105:37 | ... call to -(_:_:) ... | This NSString length is used in a String, but it may not be equivalent. | -| StringLengthConflation.swift:111:23:111:35 | ... call to -(_:_:) ... | StringLengthConflation.swift:111:23:111:26 | .length : | StringLengthConflation.swift:111:23:111:35 | ... call to -(_:_:) ... | This NSString length is used in a String, but it may not be equivalent. | -| StringLengthConflation.swift:117:22:117:34 | ... call to -(_:_:) ... | StringLengthConflation.swift:117:22:117:25 | .length : | StringLengthConflation.swift:117:22:117:34 | ... call to -(_:_:) ... | This NSString length is used in a String, but it may not be equivalent. | -| StringLengthConflation.swift:122:34:122:44 | ... call to -(_:_:) ... | StringLengthConflation.swift:122:34:122:36 | .count : | StringLengthConflation.swift:122:34:122:44 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | -| StringLengthConflation.swift:123:36:123:46 | ... call to -(_:_:) ... | StringLengthConflation.swift:123:36:123:38 | .count : | StringLengthConflation.swift:123:36:123:46 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | -| StringLengthConflation.swift:128:36:128:46 | ... call to -(_:_:) ... | StringLengthConflation.swift:128:36:128:38 | .count : | StringLengthConflation.swift:128:36:128:46 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | -| StringLengthConflation.swift:129:38:129:48 | ... call to -(_:_:) ... | StringLengthConflation.swift:129:38:129:40 | .count : | StringLengthConflation.swift:129:38:129:48 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | -| StringLengthConflation.swift:134:34:134:44 | ... call to -(_:_:) ... | StringLengthConflation.swift:134:34:134:36 | .count : | StringLengthConflation.swift:134:34:134:44 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | -| StringLengthConflation.swift:135:36:135:46 | ... call to -(_:_:) ... | StringLengthConflation.swift:135:36:135:38 | .count : | StringLengthConflation.swift:135:36:135:46 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | -| StringLengthConflation.swift:141:28:141:38 | ... call to -(_:_:) ... | StringLengthConflation.swift:141:28:141:30 | .count : | StringLengthConflation.swift:141:28:141:38 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | +| StringLengthConflation.swift:96:28:96:40 | ... call to -(_:_:) ... | StringLengthConflation.swift:96:28:96:31 | .length : | StringLengthConflation.swift:96:28:96:40 | ... call to -(_:_:) ... | This NSString length is used in a String, but it may not be equivalent. | +| StringLengthConflation.swift:100:27:100:39 | ... call to -(_:_:) ... | StringLengthConflation.swift:100:27:100:30 | .length : | StringLengthConflation.swift:100:27:100:39 | ... call to -(_:_:) ... | This NSString length is used in a String, but it may not be equivalent. | +| StringLengthConflation.swift:104:25:104:37 | ... call to -(_:_:) ... | StringLengthConflation.swift:104:25:104:28 | .length : | StringLengthConflation.swift:104:25:104:37 | ... call to -(_:_:) ... | This NSString length is used in a String, but it may not be equivalent. | +| StringLengthConflation.swift:108:25:108:37 | ... call to -(_:_:) ... | StringLengthConflation.swift:108:25:108:28 | .length : | StringLengthConflation.swift:108:25:108:37 | ... call to -(_:_:) ... | This NSString length is used in a String, but it may not be equivalent. | +| StringLengthConflation.swift:114:23:114:35 | ... call to -(_:_:) ... | StringLengthConflation.swift:114:23:114:26 | .length : | StringLengthConflation.swift:114:23:114:35 | ... call to -(_:_:) ... | This NSString length is used in a String, but it may not be equivalent. | +| StringLengthConflation.swift:120:22:120:34 | ... call to -(_:_:) ... | StringLengthConflation.swift:120:22:120:25 | .length : | StringLengthConflation.swift:120:22:120:34 | ... call to -(_:_:) ... | This NSString length is used in a String, but it may not be equivalent. | +| StringLengthConflation.swift:125:34:125:44 | ... call to -(_:_:) ... | StringLengthConflation.swift:125:34:125:36 | .count : | StringLengthConflation.swift:125:34:125:44 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | +| StringLengthConflation.swift:126:36:126:46 | ... call to -(_:_:) ... | StringLengthConflation.swift:126:36:126:38 | .count : | StringLengthConflation.swift:126:36:126:46 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | +| StringLengthConflation.swift:131:36:131:46 | ... call to -(_:_:) ... | StringLengthConflation.swift:131:36:131:38 | .count : | StringLengthConflation.swift:131:36:131:46 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | +| StringLengthConflation.swift:132:38:132:48 | ... call to -(_:_:) ... | StringLengthConflation.swift:132:38:132:40 | .count : | StringLengthConflation.swift:132:38:132:48 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | +| StringLengthConflation.swift:137:34:137:44 | ... call to -(_:_:) ... | StringLengthConflation.swift:137:34:137:36 | .count : | StringLengthConflation.swift:137:34:137:44 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | +| StringLengthConflation.swift:138:36:138:46 | ... call to -(_:_:) ... | StringLengthConflation.swift:138:36:138:38 | .count : | StringLengthConflation.swift:138:36:138:46 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | +| StringLengthConflation.swift:144:28:144:38 | ... call to -(_:_:) ... | StringLengthConflation.swift:144:28:144:30 | .count : | StringLengthConflation.swift:144:28:144:38 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | diff --git a/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.swift b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.swift index 527cacd772b..831ea864916 100644 --- a/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.swift +++ b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.swift @@ -76,16 +76,19 @@ func test(s: String) { let range5 = NSRange(location: 0, length: ns.length) // GOOD let range6 = NSRange(location: 0, length: s.count) // BAD: String length used in NSMakeRange - print("NSRange '\(range5.description)' / '\(range6.description)'") + let range7 = NSRange(location: 0, length: s.utf8.count) // BAD: String.utf8 length used in NSMakeRange [NOT DETECTED] + let range8 = NSRange(location: 0, length: s.utf16.count) // BAD: String.utf16 length used in NSMakeRange [NOT DETECTED] + let range9 = NSRange(location: 0, length: s.unicodeScalars.count) // BAD: String.unicodeScalars length used in NSMakeRange [NOT DETECTED] + print("NSRange '\(range5.description)' / '\(range6.description)' '\(range7.description)' '\(range8.description)' '\(range9.description)'") // --- converting Range to NSRange --- - let range7 = s.startIndex ..< s.endIndex - let range8 = NSRange(range7, in: s) // GOOD - let location = s.distance(from: s.startIndex, to: range7.lowerBound) - let length = s.distance(from: range7.lowerBound, to: range7.upperBound) - let range9 = NSRange(location: location, length: length) // BAD [NOT DETECTED] - print("NSRange '\(range8.description)' / '\(range9.description)'") + let range10 = s.startIndex ..< s.endIndex + let range11 = NSRange(range10, in: s) // GOOD + let location = s.distance(from: s.startIndex, to: range10.lowerBound) + let length = s.distance(from: range10.lowerBound, to: range10.upperBound) + let range12 = NSRange(location: location, length: length) // BAD [NOT DETECTED] + print("NSRange '\(range11.description)' / '\(range12.description)'") // --- String operations using an integer directly --- From 89d5bbb8e07575646e126685d6705dab50dc9963 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 27 Jul 2022 15:19:07 +0100 Subject: [PATCH 336/505] Swift: Generalize the flow states in this query. --- .../CWE-135/StringLengthConflation.ql | 55 ++++++++++++++----- 1 file changed, 42 insertions(+), 13 deletions(-) diff --git a/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql b/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql index 398c48f01e5..0b942f88d77 100644 --- a/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql +++ b/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql @@ -14,6 +14,24 @@ import swift import codeql.swift.dataflow.DataFlow import DataFlow::PathGraph +/** + * A flow state for this query, which is a type of Swift string encoding. + */ +class StringLengthConflationFlowState extends string { + string singular; + + StringLengthConflationFlowState() { + this = "String" and singular = "a String" + or + this = "NSString" and singular = "an NSString" + } + + /** + * Gets text for the singular form of this flow state. + */ + string getSingular() { result = singular } +} + /** * A configuration for tracking string lengths originating from source that is * a `String` or an `NSString` object, to a sink of a different kind that @@ -40,7 +58,11 @@ class StringLengthConflationConfiguration extends DataFlow::Configuration { ) } - override predicate isSink(DataFlow::Node node, string flowstate) { + /** + * Holds if `node` is a sink and `flowstate` is the *correct* flow state for + * that sink. We actually want to report incorrect flow states. + */ + predicate isSinkImpl(DataFlow::Node node, string flowstate) { // arguments to method calls... exists( string className, string methodName, string paramName, ClassDecl c, AbstractFunctionDecl f, @@ -78,7 +100,7 @@ class StringLengthConflationConfiguration extends DataFlow::Configuration { f.getName() = methodName and f.getParam(pragma[only_bind_into](arg)).getName() = paramName and call.getArgument(pragma[only_bind_into](arg)).getExpr() = node.asExpr() and - flowstate = "String" // `String` length flowing into `NSString` + flowstate = "NSString" ) or // arguments to function calls... @@ -89,7 +111,7 @@ class StringLengthConflationConfiguration extends DataFlow::Configuration { call.getStaticTarget().getName() = funcName and call.getStaticTarget().getParam(pragma[only_bind_into](arg)).getName() = paramName and call.getArgument(pragma[only_bind_into](arg)).getExpr() = node.asExpr() and - flowstate = "String" // `String` length flowing into `NSString` + flowstate = "NSString" ) or // arguments to function calls... @@ -122,7 +144,16 @@ class StringLengthConflationConfiguration extends DataFlow::Configuration { .getParam(pragma[only_bind_into](arg)) .getName() = paramName and call.getArgument(pragma[only_bind_into](arg)).getExpr() = node.asExpr() and - flowstate = "NSString" // `NSString` length flowing into `String` + flowstate = "String" + ) + } + + override predicate isSink(DataFlow::Node node, string flowstate) { + // Permit any *incorrect* flowstate, as those are the results the query + // should report. + exists(string correctFlowState | + isSinkImpl(node, correctFlowState) and + flowstate.(StringLengthConflationFlowState) != correctFlowState ) } @@ -134,15 +165,13 @@ class StringLengthConflationConfiguration extends DataFlow::Configuration { from StringLengthConflationConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink, - string flowstate, string message + StringLengthConflationFlowState sourceFlowState, StringLengthConflationFlowState sinkFlowstate, + string message where config.hasFlowPath(source, sink) and - config.isSink(sink.getNode(), flowstate) and - ( - flowstate = "String" and - message = "This String length is used in an NSString, but it may not be equivalent." - or - flowstate = "NSString" and - message = "This NSString length is used in a String, but it may not be equivalent." - ) + config.isSource(source.getNode(), sourceFlowState) and + config.isSinkImpl(sink.getNode(), sinkFlowstate) and + message = + "This " + sourceFlowState + " length is used in " + sinkFlowstate.getSingular() + + ", but it may not be equivalent." select sink.getNode(), source, sink, message From 70ca37a3d0580aa5be32b97f958da4bf9c0d8bc4 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 27 Jul 2022 15:39:27 +0100 Subject: [PATCH 337/505] Swift: Model utf8, utf16 a\nd unicodeScalars sources. --- .../CWE-135/StringLengthConflation.ql | 30 +++++++++++++++++++ .../CWE-135/StringLengthConflation.expected | 12 ++++++++ .../CWE-135/StringLengthConflation.swift | 12 ++++---- 3 files changed, 48 insertions(+), 6 deletions(-) diff --git a/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql b/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql index 0b942f88d77..86caf7b064f 100644 --- a/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql +++ b/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql @@ -24,6 +24,12 @@ class StringLengthConflationFlowState extends string { this = "String" and singular = "a String" or this = "NSString" and singular = "an NSString" + or + this = "String.utf8" and singular = "a String.utf8" + or + this = "String.utf16" and singular = "a String.utf16" + or + this = "String.unicodeScalars" and singular = "a String.unicodeScalars" } /** @@ -56,6 +62,30 @@ class StringLengthConflationConfiguration extends DataFlow::Configuration { node.asExpr() = member and flowstate = "NSString" ) + or + // result of a call to `String.utf8.count` + exists(MemberRefExpr member | + member.getBaseExpr().getType().getName() = "String.UTF8View" and + member.getMember().(VarDecl).getName() = "count" and + node.asExpr() = member and + flowstate = "String.utf8" + ) + or + // result of a call to `String.utf16.count` + exists(MemberRefExpr member | + member.getBaseExpr().getType().getName() = "String.UTF16View" and + member.getMember().(VarDecl).getName() = "count" and + node.asExpr() = member and + flowstate = "String.utf16" + ) + or + // result of a call to `String.unicodeScalars.count` + exists(MemberRefExpr member | + member.getBaseExpr().getType().getName() = "String.UnicodeScalarView" and + member.getMember().(VarDecl).getName() = "count" and + node.asExpr() = member and + flowstate = "String.unicodeScalars" + ) } /** diff --git a/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.expected b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.expected index dec1e4799c7..c9a523c48a0 100644 --- a/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.expected +++ b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.expected @@ -16,12 +16,18 @@ edges | StringLengthConflation.swift:144:28:144:30 | .count : | StringLengthConflation.swift:144:28:144:38 | ... call to -(_:_:) ... | nodes | StringLengthConflation.swift:53:43:53:46 | .length | semmle.label | .length | +| StringLengthConflation.swift:54:43:54:50 | .count | semmle.label | .count | +| StringLengthConflation.swift:55:43:55:51 | .count | semmle.label | .count | +| StringLengthConflation.swift:56:43:56:60 | .count | semmle.label | .count | | StringLengthConflation.swift:60:47:60:50 | .length : | semmle.label | .length : | | StringLengthConflation.swift:60:47:60:59 | ... call to /(_:_:) ... | semmle.label | ... call to /(_:_:) ... | | StringLengthConflation.swift:66:33:66:36 | .length : | semmle.label | .length : | | StringLengthConflation.swift:66:33:66:45 | ... call to /(_:_:) ... | semmle.label | ... call to /(_:_:) ... | | StringLengthConflation.swift:72:33:72:35 | .count | semmle.label | .count | | StringLengthConflation.swift:78:47:78:49 | .count | semmle.label | .count | +| StringLengthConflation.swift:79:47:79:54 | .count | semmle.label | .count | +| StringLengthConflation.swift:80:47:80:55 | .count | semmle.label | .count | +| StringLengthConflation.swift:81:47:81:64 | .count | semmle.label | .count | | StringLengthConflation.swift:96:28:96:31 | .length : | semmle.label | .length : | | StringLengthConflation.swift:96:28:96:40 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | | StringLengthConflation.swift:100:27:100:30 | .length : | semmle.label | .length : | @@ -51,10 +57,16 @@ nodes subpaths #select | StringLengthConflation.swift:53:43:53:46 | .length | StringLengthConflation.swift:53:43:53:46 | .length | StringLengthConflation.swift:53:43:53:46 | .length | This NSString length is used in a String, but it may not be equivalent. | +| StringLengthConflation.swift:54:43:54:50 | .count | StringLengthConflation.swift:54:43:54:50 | .count | StringLengthConflation.swift:54:43:54:50 | .count | This String.utf8 length is used in a String, but it may not be equivalent. | +| StringLengthConflation.swift:55:43:55:51 | .count | StringLengthConflation.swift:55:43:55:51 | .count | StringLengthConflation.swift:55:43:55:51 | .count | This String.utf16 length is used in a String, but it may not be equivalent. | +| StringLengthConflation.swift:56:43:56:60 | .count | StringLengthConflation.swift:56:43:56:60 | .count | StringLengthConflation.swift:56:43:56:60 | .count | This String.unicodeScalars length is used in a String, but it may not be equivalent. | | StringLengthConflation.swift:60:47:60:59 | ... call to /(_:_:) ... | StringLengthConflation.swift:60:47:60:50 | .length : | StringLengthConflation.swift:60:47:60:59 | ... call to /(_:_:) ... | This NSString length is used in a String, but it may not be equivalent. | | StringLengthConflation.swift:66:33:66:45 | ... call to /(_:_:) ... | StringLengthConflation.swift:66:33:66:36 | .length : | StringLengthConflation.swift:66:33:66:45 | ... call to /(_:_:) ... | This NSString length is used in a String, but it may not be equivalent. | | StringLengthConflation.swift:72:33:72:35 | .count | StringLengthConflation.swift:72:33:72:35 | .count | StringLengthConflation.swift:72:33:72:35 | .count | This String length is used in an NSString, but it may not be equivalent. | | StringLengthConflation.swift:78:47:78:49 | .count | StringLengthConflation.swift:78:47:78:49 | .count | StringLengthConflation.swift:78:47:78:49 | .count | This String length is used in an NSString, but it may not be equivalent. | +| StringLengthConflation.swift:79:47:79:54 | .count | StringLengthConflation.swift:79:47:79:54 | .count | StringLengthConflation.swift:79:47:79:54 | .count | This String.utf8 length is used in an NSString, but it may not be equivalent. | +| StringLengthConflation.swift:80:47:80:55 | .count | StringLengthConflation.swift:80:47:80:55 | .count | StringLengthConflation.swift:80:47:80:55 | .count | This String.utf16 length is used in an NSString, but it may not be equivalent. | +| StringLengthConflation.swift:81:47:81:64 | .count | StringLengthConflation.swift:81:47:81:64 | .count | StringLengthConflation.swift:81:47:81:64 | .count | This String.unicodeScalars length is used in an NSString, but it may not be equivalent. | | StringLengthConflation.swift:96:28:96:40 | ... call to -(_:_:) ... | StringLengthConflation.swift:96:28:96:31 | .length : | StringLengthConflation.swift:96:28:96:40 | ... call to -(_:_:) ... | This NSString length is used in a String, but it may not be equivalent. | | StringLengthConflation.swift:100:27:100:39 | ... call to -(_:_:) ... | StringLengthConflation.swift:100:27:100:30 | .length : | StringLengthConflation.swift:100:27:100:39 | ... call to -(_:_:) ... | This NSString length is used in a String, but it may not be equivalent. | | StringLengthConflation.swift:104:25:104:37 | ... call to -(_:_:) ... | StringLengthConflation.swift:104:25:104:28 | .length : | StringLengthConflation.swift:104:25:104:37 | ... call to -(_:_:) ... | This NSString length is used in a String, but it may not be equivalent. | diff --git a/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.swift b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.swift index 831ea864916..5495a44988a 100644 --- a/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.swift +++ b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.swift @@ -51,9 +51,9 @@ func test(s: String) { let ix1 = String.Index(encodedOffset: s.count) // GOOD let ix2 = String.Index(encodedOffset: ns.length) // BAD: NSString length used in String.Index - let ix3 = String.Index(encodedOffset: s.utf8.count) // BAD: String.utf8 length used in String.Index [NOT DETECTED] - let ix4 = String.Index(encodedOffset: s.utf16.count) // BAD: String.utf16 length used in String.Index [NOT DETECTED] - let ix5 = String.Index(encodedOffset: s.unicodeScalars.count) // BAD: string.unicodeScalars length used in String.Index [NOT DETECTED] + let ix3 = String.Index(encodedOffset: s.utf8.count) // BAD: String.utf8 length used in String.Index + let ix4 = String.Index(encodedOffset: s.utf16.count) // BAD: String.utf16 length used in String.Index + let ix5 = String.Index(encodedOffset: s.unicodeScalars.count) // BAD: string.unicodeScalars length used in String.Index print("String.Index '\(ix1.encodedOffset)' / '\(ix2.encodedOffset)' '\(ix3.encodedOffset)' '\(ix4.encodedOffset)' '\(ix5.encodedOffset)'") let ix6 = s.index(s.startIndex, offsetBy: s.count / 2) // GOOD @@ -76,9 +76,9 @@ func test(s: String) { let range5 = NSRange(location: 0, length: ns.length) // GOOD let range6 = NSRange(location: 0, length: s.count) // BAD: String length used in NSMakeRange - let range7 = NSRange(location: 0, length: s.utf8.count) // BAD: String.utf8 length used in NSMakeRange [NOT DETECTED] - let range8 = NSRange(location: 0, length: s.utf16.count) // BAD: String.utf16 length used in NSMakeRange [NOT DETECTED] - let range9 = NSRange(location: 0, length: s.unicodeScalars.count) // BAD: String.unicodeScalars length used in NSMakeRange [NOT DETECTED] + let range7 = NSRange(location: 0, length: s.utf8.count) // BAD: String.utf8 length used in NSMakeRange + let range8 = NSRange(location: 0, length: s.utf16.count) // BAD: String.utf16 length used in NSMakeRange + let range9 = NSRange(location: 0, length: s.unicodeScalars.count) // BAD: String.unicodeScalars length used in NSMakeRange print("NSRange '\(range5.description)' / '\(range6.description)' '\(range7.description)' '\(range8.description)' '\(range9.description)'") // --- converting Range to NSRange --- From fe69bbf17ccadacf6b09c5055f82b97eb62065ab Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 27 Jul 2022 17:44:57 +0100 Subject: [PATCH 338/505] Swift: It turns out NSString.length always exactly matches String.utf16.count. --- .../CWE-135/StringLengthConflation.ql | 22 ++++++++++++++----- .../CWE-135/StringLengthConflation.expected | 2 -- .../CWE-135/StringLengthConflation.swift | 2 +- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql b/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql index 86caf7b064f..7ab5826ba82 100644 --- a/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql +++ b/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql @@ -18,20 +18,29 @@ import DataFlow::PathGraph * A flow state for this query, which is a type of Swift string encoding. */ class StringLengthConflationFlowState extends string { + string equivClass; string singular; StringLengthConflationFlowState() { - this = "String" and singular = "a String" + this = "String" and singular = "a String" and equivClass = "String" or - this = "NSString" and singular = "an NSString" + this = "NSString" and singular = "an NSString" and equivClass = "NSString" or - this = "String.utf8" and singular = "a String.utf8" + this = "String.utf8" and singular = "a String.utf8" and equivClass = "String.utf8" or - this = "String.utf16" and singular = "a String.utf16" + this = "String.utf16" and singular = "a String.utf16" and equivClass = "NSString" or - this = "String.unicodeScalars" and singular = "a String.unicodeScalars" + this = "String.unicodeScalars" and + singular = "a String.unicodeScalars" and + equivClass = "String.unicodeScalars" } + /** + * Gets the equivalence class for this flow state. If these are equal, + * they should be treated as equivalent. + */ + string getEquivClass() { result = equivClass } + /** * Gets text for the singular form of this flow state. */ @@ -183,7 +192,8 @@ class StringLengthConflationConfiguration extends DataFlow::Configuration { // should report. exists(string correctFlowState | isSinkImpl(node, correctFlowState) and - flowstate.(StringLengthConflationFlowState) != correctFlowState + flowstate.(StringLengthConflationFlowState).getEquivClass() != + correctFlowState.(StringLengthConflationFlowState).getEquivClass() ) } diff --git a/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.expected b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.expected index c9a523c48a0..a648f99b728 100644 --- a/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.expected +++ b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.expected @@ -26,7 +26,6 @@ nodes | StringLengthConflation.swift:72:33:72:35 | .count | semmle.label | .count | | StringLengthConflation.swift:78:47:78:49 | .count | semmle.label | .count | | StringLengthConflation.swift:79:47:79:54 | .count | semmle.label | .count | -| StringLengthConflation.swift:80:47:80:55 | .count | semmle.label | .count | | StringLengthConflation.swift:81:47:81:64 | .count | semmle.label | .count | | StringLengthConflation.swift:96:28:96:31 | .length : | semmle.label | .length : | | StringLengthConflation.swift:96:28:96:40 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | @@ -65,7 +64,6 @@ subpaths | StringLengthConflation.swift:72:33:72:35 | .count | StringLengthConflation.swift:72:33:72:35 | .count | StringLengthConflation.swift:72:33:72:35 | .count | This String length is used in an NSString, but it may not be equivalent. | | StringLengthConflation.swift:78:47:78:49 | .count | StringLengthConflation.swift:78:47:78:49 | .count | StringLengthConflation.swift:78:47:78:49 | .count | This String length is used in an NSString, but it may not be equivalent. | | StringLengthConflation.swift:79:47:79:54 | .count | StringLengthConflation.swift:79:47:79:54 | .count | StringLengthConflation.swift:79:47:79:54 | .count | This String.utf8 length is used in an NSString, but it may not be equivalent. | -| StringLengthConflation.swift:80:47:80:55 | .count | StringLengthConflation.swift:80:47:80:55 | .count | StringLengthConflation.swift:80:47:80:55 | .count | This String.utf16 length is used in an NSString, but it may not be equivalent. | | StringLengthConflation.swift:81:47:81:64 | .count | StringLengthConflation.swift:81:47:81:64 | .count | StringLengthConflation.swift:81:47:81:64 | .count | This String.unicodeScalars length is used in an NSString, but it may not be equivalent. | | StringLengthConflation.swift:96:28:96:40 | ... call to -(_:_:) ... | StringLengthConflation.swift:96:28:96:31 | .length : | StringLengthConflation.swift:96:28:96:40 | ... call to -(_:_:) ... | This NSString length is used in a String, but it may not be equivalent. | | StringLengthConflation.swift:100:27:100:39 | ... call to -(_:_:) ... | StringLengthConflation.swift:100:27:100:30 | .length : | StringLengthConflation.swift:100:27:100:39 | ... call to -(_:_:) ... | This NSString length is used in a String, but it may not be equivalent. | diff --git a/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.swift b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.swift index 5495a44988a..4321b661fa4 100644 --- a/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.swift +++ b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.swift @@ -77,7 +77,7 @@ func test(s: String) { let range5 = NSRange(location: 0, length: ns.length) // GOOD let range6 = NSRange(location: 0, length: s.count) // BAD: String length used in NSMakeRange let range7 = NSRange(location: 0, length: s.utf8.count) // BAD: String.utf8 length used in NSMakeRange - let range8 = NSRange(location: 0, length: s.utf16.count) // BAD: String.utf16 length used in NSMakeRange + let range8 = NSRange(location: 0, length: s.utf16.count) // GOOD: String.utf16 length and NSRange count are equivalent let range9 = NSRange(location: 0, length: s.unicodeScalars.count) // BAD: String.unicodeScalars length used in NSMakeRange print("NSRange '\(range5.description)' / '\(range6.description)' '\(range7.description)' '\(range8.description)' '\(range9.description)'") From 9b26921cb67a03a6dd3958e0f42b6941523072c3 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Thu, 28 Jul 2022 09:04:12 +0200 Subject: [PATCH 339/505] Control flow: add order disambuigation customization --- .../internal/ControlFlowGraphImplShared.qll | 13 ++++++++++--- .../internal/ControlFlowGraphImplShared.qll | 13 ++++++++++--- .../internal/ControlFlowGraphImplShared.qll | 13 ++++++++++--- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll index 13fb796ca3d..7d0dd10c084 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll @@ -881,7 +881,12 @@ import Cached * graph is restricted to nodes from `RelevantNode`. */ module TestOutput { - abstract class RelevantNode extends Node { } + abstract class RelevantNode extends Node { + /** + * Gets a string used to resolve ties in node and edge ordering. + */ + string getOrderDisambuigation() { result = "" } + } query predicate nodes(RelevantNode n, string attr, string val) { attr = "semmle.order" and @@ -894,7 +899,8 @@ module TestOutput { p order by l.getFile().getBaseName(), l.getFile().getAbsolutePath(), l.getStartLine(), - l.getStartColumn(), l.getEndLine(), l.getEndColumn(), p.toString() + l.getStartColumn(), l.getEndLine(), l.getEndColumn(), p.toString(), + p.getOrderDisambuigation() ) ).toString() } @@ -916,7 +922,8 @@ module TestOutput { s order by l.getFile().getBaseName(), l.getFile().getAbsolutePath(), l.getStartLine(), - l.getStartColumn(), l.getEndLine(), l.getEndColumn(), t.toString(), s.toString() + l.getStartColumn(), l.getEndLine(), l.getEndColumn(), t.toString(), s.toString(), + s.getOrderDisambuigation() ) ).toString() } diff --git a/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplShared.qll b/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplShared.qll index 13fb796ca3d..7d0dd10c084 100644 --- a/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplShared.qll +++ b/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplShared.qll @@ -881,7 +881,12 @@ import Cached * graph is restricted to nodes from `RelevantNode`. */ module TestOutput { - abstract class RelevantNode extends Node { } + abstract class RelevantNode extends Node { + /** + * Gets a string used to resolve ties in node and edge ordering. + */ + string getOrderDisambuigation() { result = "" } + } query predicate nodes(RelevantNode n, string attr, string val) { attr = "semmle.order" and @@ -894,7 +899,8 @@ module TestOutput { p order by l.getFile().getBaseName(), l.getFile().getAbsolutePath(), l.getStartLine(), - l.getStartColumn(), l.getEndLine(), l.getEndColumn(), p.toString() + l.getStartColumn(), l.getEndLine(), l.getEndColumn(), p.toString(), + p.getOrderDisambuigation() ) ).toString() } @@ -916,7 +922,8 @@ module TestOutput { s order by l.getFile().getBaseName(), l.getFile().getAbsolutePath(), l.getStartLine(), - l.getStartColumn(), l.getEndLine(), l.getEndColumn(), t.toString(), s.toString() + l.getStartColumn(), l.getEndLine(), l.getEndColumn(), t.toString(), s.toString(), + s.getOrderDisambuigation() ) ).toString() } diff --git a/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImplShared.qll b/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImplShared.qll index 13fb796ca3d..7d0dd10c084 100644 --- a/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImplShared.qll +++ b/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImplShared.qll @@ -881,7 +881,12 @@ import Cached * graph is restricted to nodes from `RelevantNode`. */ module TestOutput { - abstract class RelevantNode extends Node { } + abstract class RelevantNode extends Node { + /** + * Gets a string used to resolve ties in node and edge ordering. + */ + string getOrderDisambuigation() { result = "" } + } query predicate nodes(RelevantNode n, string attr, string val) { attr = "semmle.order" and @@ -894,7 +899,8 @@ module TestOutput { p order by l.getFile().getBaseName(), l.getFile().getAbsolutePath(), l.getStartLine(), - l.getStartColumn(), l.getEndLine(), l.getEndColumn(), p.toString() + l.getStartColumn(), l.getEndLine(), l.getEndColumn(), p.toString(), + p.getOrderDisambuigation() ) ).toString() } @@ -916,7 +922,8 @@ module TestOutput { s order by l.getFile().getBaseName(), l.getFile().getAbsolutePath(), l.getStartLine(), - l.getStartColumn(), l.getEndLine(), l.getEndColumn(), t.toString(), s.toString() + l.getStartColumn(), l.getEndLine(), l.getEndColumn(), t.toString(), s.toString(), + s.getOrderDisambuigation() ) ).toString() } From e5342867c6b443b5488a82894614c1456c8d2e2f Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 28 Jul 2022 09:52:33 +0100 Subject: [PATCH 340/505] Swift: Add a note to the qhelp. --- .../src/queries/Security/CWE-135/StringLengthConflation.qhelp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.qhelp b/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.qhelp index a13dbd80152..15e99e7e407 100644 --- a/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.qhelp +++ b/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.qhelp @@ -5,6 +5,8 @@

    Using a length value from an NSString in a String, or a count from a String in an NSString, may cause unexpected behavior including (in some cases) buffer overwrites. This is because certain unicode sequences are represented as one character in a String but as a sequence of multiple characters in an NSString. For example, a 'thumbs up' emoji with a skin tone modifier (👍🏿) is represented as U+1F44D (👍) then the modifier U+1F3FF.

    +

    This issue can also arise from using the values of String.utf8.count, String.utf16.count or String.unicodeScalars.count in an unsuitable place.

    + From 6cd6f74be97b0d9fbde01604316e002c79899817 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 28 Jul 2022 10:13:04 +0100 Subject: [PATCH 341/505] Swift: Repair predicate lost in merge. --- .../queries/Security/CWE-135/StringLengthConflation.ql | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql b/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql index eeee583dd6f..c9bdff012ce 100644 --- a/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql +++ b/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql @@ -179,6 +179,16 @@ class StringLengthConflationConfiguration extends DataFlow::Configuration { ) } + override predicate isSink(DataFlow::Node node, string flowstate) { + // Permit any *incorrect* flowstate, as those are the results the query + // should report. + exists(string correctFlowState | + isSinkImpl(node, correctFlowState) and + flowstate.(StringLengthConflationFlowState).getEquivClass() != + correctFlowState.(StringLengthConflationFlowState).getEquivClass() + ) + } + override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { // allow flow through `+`, `-`, `*` etc. node2.asExpr().(ArithmeticOperation).getAnOperand() = node1.asExpr() From ab1370cc8f01b7bedf6bfdbbcb9bb05ef8ca291c Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Thu, 28 Jul 2022 11:19:45 +0200 Subject: [PATCH 342/505] Swift: add `--no-cleanup` to integration tests --- swift/integration-tests/create_database_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swift/integration-tests/create_database_utils.py b/swift/integration-tests/create_database_utils.py index 3f2d11a39f7..38822fd70b4 100644 --- a/swift/integration-tests/create_database_utils.py +++ b/swift/integration-tests/create_database_utils.py @@ -12,7 +12,7 @@ def run_codeql_database_create(cmds, lang, keep_trap=True): codeql_root = pathlib.Path(__file__).parents[2] cmd = [ "codeql", "database", "create", - "-s", ".", "-l", "swift", "--internal-use-lua-tracing", f"--search-path={codeql_root}", + "-s", ".", "-l", "swift", "--internal-use-lua-tracing", f"--search-path={codeql_root}", "--no-cleanup", ] if keep_trap: cmd.append("--keep-trap") From 76ea63ffbe07a68686a132d3d237004d1dfad0d4 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Thu, 28 Jul 2022 12:28:52 +0200 Subject: [PATCH 343/505] Swift: deduplicate `VarDecl` Deduplication of `ConcreteVarDecl` is triggered only if its `DeclContext` is not local. This avoids a mangled name conflict. Also added more thourough tests for `ConcreteVarDecl` and `ParamDecl`. --- swift/extractor/visitors/DeclVisitor.cpp | 29 ++++++++++++------- swift/extractor/visitors/DeclVisitor.h | 4 +-- .../ConcreteVarDecl/ConcreteVarDecl.expected | 7 +++++ .../decl/ConcreteVarDecl/ConcreteVarDecl.ql | 14 +++++++++ .../ConcreteVarDecl_getAccessorDecl.expected | 13 +++++++++ .../ConcreteVarDecl_getAccessorDecl.ql | 7 +++++ ...cl_getAttachedPropertyWrapperType.expected | 1 + ...eVarDecl_getAttachedPropertyWrapperType.ql | 7 +++++ ...creteVarDecl_getParentInitializer.expected | 3 ++ .../ConcreteVarDecl_getParentInitializer.ql | 7 +++++ .../ConcreteVarDecl_getParentPattern.expected | 7 +++++ .../ConcreteVarDecl_getParentPattern.ql | 7 +++++ .../decl/ConcreteVarDecl/MISSING_SOURCE.txt | 4 --- .../decl/ConcreteVarDecl/var_decls.swift | 25 ++++++++++++++++ .../decl/ParamDecl/MISSING_SOURCE.txt | 4 --- .../decl/ParamDecl/ParamDecl.expected | 15 ++++++++++ .../generated/decl/ParamDecl/ParamDecl.ql | 14 +++++++++ .../ParamDecl_getAccessorDecl.expected | 0 .../ParamDecl/ParamDecl_getAccessorDecl.ql | 7 +++++ ...cl_getAttachedPropertyWrapperType.expected | 0 ...aramDecl_getAttachedPropertyWrapperType.ql | 7 +++++ .../ParamDecl_getParentInitializer.expected | 0 .../ParamDecl_getParentInitializer.ql | 7 +++++ .../ParamDecl_getParentPattern.expected | 0 .../ParamDecl/ParamDecl_getParentPattern.ql | 7 +++++ .../decl/ParamDecl/param_decls.swift | 15 ++++++++++ 26 files changed, 191 insertions(+), 20 deletions(-) create mode 100644 swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl.expected create mode 100644 swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl.ql create mode 100644 swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getAccessorDecl.expected create mode 100644 swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getAccessorDecl.ql create mode 100644 swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getAttachedPropertyWrapperType.expected create mode 100644 swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getAttachedPropertyWrapperType.ql create mode 100644 swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getParentInitializer.expected create mode 100644 swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getParentInitializer.ql create mode 100644 swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getParentPattern.expected create mode 100644 swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getParentPattern.ql delete mode 100644 swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/MISSING_SOURCE.txt create mode 100644 swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/var_decls.swift delete mode 100644 swift/ql/test/extractor-tests/generated/decl/ParamDecl/MISSING_SOURCE.txt create mode 100644 swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl.expected create mode 100644 swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl.ql create mode 100644 swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getAccessorDecl.expected create mode 100644 swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getAccessorDecl.ql create mode 100644 swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getAttachedPropertyWrapperType.expected create mode 100644 swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getAttachedPropertyWrapperType.ql create mode 100644 swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getParentInitializer.expected create mode 100644 swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getParentInitializer.ql create mode 100644 swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getParentPattern.expected create mode 100644 swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getParentPattern.ql create mode 100644 swift/ql/test/extractor-tests/generated/decl/ParamDecl/param_decls.swift diff --git a/swift/extractor/visitors/DeclVisitor.cpp b/swift/extractor/visitors/DeclVisitor.cpp index 819c959a6d2..dc693e66a0f 100644 --- a/swift/extractor/visitors/DeclVisitor.cpp +++ b/swift/extractor/visitors/DeclVisitor.cpp @@ -83,11 +83,13 @@ codeql::PrecedenceGroupDecl DeclVisitor::translatePrecedenceGroupDecl( return entry; } -codeql::ParamDecl DeclVisitor::translateParamDecl(const swift::ParamDecl& decl) { - // TODO: deduplicate - ParamDecl entry{dispatcher_.assignNewLabel(decl)}; - fillVarDecl(decl, entry); - entry.is_inout = decl.isInOut(); +std::optional DeclVisitor::translateParamDecl(const swift::ParamDecl& decl) { + auto entry = createNamedEntry(decl); + if (!entry) { + return std::nullopt; + } + fillVarDecl(decl, *entry); + entry->is_inout = decl.isInOut(); return entry; } @@ -111,11 +113,18 @@ codeql::PatternBindingDecl DeclVisitor::translatePatternBindingDecl( return entry; } -codeql::ConcreteVarDecl DeclVisitor::translateVarDecl(const swift::VarDecl& decl) { - // TODO: deduplicate all non-local variables - ConcreteVarDecl entry{dispatcher_.assignNewLabel(decl)}; - entry.introducer_int = static_cast(decl.getIntroducer()); - fillVarDecl(decl, entry); +std::optional DeclVisitor::translateVarDecl(const swift::VarDecl& decl) { + std::optional entry; + if (decl.getDeclContext()->isLocalContext()) { + entry.emplace(dispatcher_.assignNewLabel(decl)); + } else { + entry = createNamedEntry(decl); + if (!entry) { + return std::nullopt; + } + } + entry->introducer_int = static_cast(decl.getIntroducer()); + fillVarDecl(decl, *entry); return entry; } diff --git a/swift/extractor/visitors/DeclVisitor.h b/swift/extractor/visitors/DeclVisitor.h index 0d6296591a0..85e819adb5b 100644 --- a/swift/extractor/visitors/DeclVisitor.h +++ b/swift/extractor/visitors/DeclVisitor.h @@ -30,10 +30,10 @@ class DeclVisitor : public AstVisitorBase { codeql::PostfixOperatorDecl translatePostfixOperatorDecl(const swift::PostfixOperatorDecl& decl); codeql::InfixOperatorDecl translateInfixOperatorDecl(const swift::InfixOperatorDecl& decl); codeql::PrecedenceGroupDecl translatePrecedenceGroupDecl(const swift::PrecedenceGroupDecl& decl); - codeql::ParamDecl translateParamDecl(const swift::ParamDecl& decl); + std::optional translateParamDecl(const swift::ParamDecl& decl); codeql::TopLevelCodeDecl translateTopLevelCodeDecl(const swift::TopLevelCodeDecl& decl); codeql::PatternBindingDecl translatePatternBindingDecl(const swift::PatternBindingDecl& decl); - codeql::ConcreteVarDecl translateVarDecl(const swift::VarDecl& decl); + std::optional translateVarDecl(const swift::VarDecl& decl); std::variant translateStructDecl( const swift::StructDecl& decl); std::variant translateClassDecl( diff --git a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl.expected b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl.expected new file mode 100644 index 00000000000..f50475445e5 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl.expected @@ -0,0 +1,7 @@ +| var_decls.swift:4:7:4:7 | i | getInterfaceType: | Int | getName: | i | getType: | Int | getIntroducerInt: | 1 | +| var_decls.swift:7:5:7:5 | numbers | getInterfaceType: | [Int] | getName: | numbers | getType: | [Int] | getIntroducerInt: | 1 | +| var_decls.swift:10:12:10:12 | numbers | getInterfaceType: | [Int] | getName: | numbers | getType: | [Int] | getIntroducerInt: | 0 | +| var_decls.swift:15:7:15:7 | wrappedValue | getInterfaceType: | T | getName: | wrappedValue | getType: | T | getIntroducerInt: | 1 | +| var_decls.swift:20:7:20:7 | wrappedValue | getInterfaceType: | Int | getName: | wrappedValue | getType: | Int | getIntroducerInt: | 1 | +| var_decls.swift:24:15:24:15 | _wrapped | getInterfaceType: | X | getName: | _wrapped | getType: | X | getIntroducerInt: | 1 | +| var_decls.swift:24:15:24:15 | wrapped | getInterfaceType: | Int | getName: | wrapped | getType: | Int | getIntroducerInt: | 1 | diff --git a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl.ql b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl.ql new file mode 100644 index 00000000000..55ae04af6ac --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl.ql @@ -0,0 +1,14 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from ConcreteVarDecl x, Type getInterfaceType, string getName, Type getType, int getIntroducerInt +where + toBeTested(x) and + not x.isUnknown() and + getInterfaceType = x.getInterfaceType() and + getName = x.getName() and + getType = x.getType() and + getIntroducerInt = x.getIntroducerInt() +select x, "getInterfaceType:", getInterfaceType, "getName:", getName, "getType:", getType, + "getIntroducerInt:", getIntroducerInt diff --git a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getAccessorDecl.expected b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getAccessorDecl.expected new file mode 100644 index 00000000000..cfbce6fe5ef --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getAccessorDecl.expected @@ -0,0 +1,13 @@ +| var_decls.swift:10:12:10:12 | numbers | 0 | var_decls.swift:10:12:10:12 | get | +| var_decls.swift:15:7:15:7 | wrappedValue | 0 | var_decls.swift:15:7:15:7 | get | +| var_decls.swift:15:7:15:7 | wrappedValue | 1 | var_decls.swift:15:7:15:7 | set | +| var_decls.swift:15:7:15:7 | wrappedValue | 2 | var_decls.swift:15:7:15:7 | (unnamed function decl) | +| var_decls.swift:20:7:20:7 | wrappedValue | 0 | var_decls.swift:20:7:20:7 | get | +| var_decls.swift:20:7:20:7 | wrappedValue | 1 | var_decls.swift:20:7:20:7 | set | +| var_decls.swift:20:7:20:7 | wrappedValue | 2 | var_decls.swift:20:7:20:7 | (unnamed function decl) | +| var_decls.swift:24:15:24:15 | _wrapped | 0 | var_decls.swift:24:15:24:15 | get | +| var_decls.swift:24:15:24:15 | _wrapped | 1 | var_decls.swift:24:15:24:15 | set | +| var_decls.swift:24:15:24:15 | _wrapped | 2 | var_decls.swift:24:15:24:15 | (unnamed function decl) | +| var_decls.swift:24:15:24:15 | wrapped | 0 | var_decls.swift:24:15:24:15 | get | +| var_decls.swift:24:15:24:15 | wrapped | 1 | var_decls.swift:24:15:24:15 | set | +| var_decls.swift:24:15:24:15 | wrapped | 2 | var_decls.swift:24:15:24:15 | (unnamed function decl) | diff --git a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getAccessorDecl.ql b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getAccessorDecl.ql new file mode 100644 index 00000000000..b25caf7bf97 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getAccessorDecl.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from ConcreteVarDecl x, int index +where toBeTested(x) and not x.isUnknown() +select x, index, x.getAccessorDecl(index) diff --git a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getAttachedPropertyWrapperType.expected b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getAttachedPropertyWrapperType.expected new file mode 100644 index 00000000000..67bff5661b0 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getAttachedPropertyWrapperType.expected @@ -0,0 +1 @@ +| var_decls.swift:24:15:24:15 | wrapped | X | diff --git a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getAttachedPropertyWrapperType.ql b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getAttachedPropertyWrapperType.ql new file mode 100644 index 00000000000..569c25f0602 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getAttachedPropertyWrapperType.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from ConcreteVarDecl x +where toBeTested(x) and not x.isUnknown() +select x, x.getAttachedPropertyWrapperType() diff --git a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getParentInitializer.expected b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getParentInitializer.expected new file mode 100644 index 00000000000..786b16fcab1 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getParentInitializer.expected @@ -0,0 +1,3 @@ +| var_decls.swift:4:7:4:7 | i | var_decls.swift:4:11:4:11 | 0 | +| var_decls.swift:7:5:7:5 | numbers | var_decls.swift:7:15:7:18 | [...] | +| var_decls.swift:10:12:10:12 | numbers | var_decls.swift:10:22:10:35 | [...] | diff --git a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getParentInitializer.ql b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getParentInitializer.ql new file mode 100644 index 00000000000..eddfa346732 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getParentInitializer.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from ConcreteVarDecl x +where toBeTested(x) and not x.isUnknown() +select x, x.getParentInitializer() diff --git a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getParentPattern.expected b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getParentPattern.expected new file mode 100644 index 00000000000..8e15539394a --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getParentPattern.expected @@ -0,0 +1,7 @@ +| var_decls.swift:4:7:4:7 | i | var_decls.swift:4:7:4:7 | i | +| var_decls.swift:7:5:7:5 | numbers | var_decls.swift:7:5:7:5 | numbers | +| var_decls.swift:10:12:10:12 | numbers | var_decls.swift:10:12:10:12 | numbers | +| var_decls.swift:15:7:15:7 | wrappedValue | var_decls.swift:15:7:15:21 | ... as ... | +| var_decls.swift:20:7:20:7 | wrappedValue | var_decls.swift:20:7:20:21 | ... as ... | +| var_decls.swift:24:15:24:15 | _wrapped | var_decls.swift:24:15:24:15 | ... as ... | +| var_decls.swift:24:15:24:15 | wrapped | var_decls.swift:24:15:24:25 | ... as ... | diff --git a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getParentPattern.ql b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getParentPattern.ql new file mode 100644 index 00000000000..2aedb61e2ae --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getParentPattern.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from ConcreteVarDecl x +where toBeTested(x) and not x.isUnknown() +select x, x.getParentPattern() diff --git a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/var_decls.swift b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/var_decls.swift new file mode 100644 index 00000000000..37c145a5a42 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/var_decls.swift @@ -0,0 +1,25 @@ +func loop() { + for i in 1...5 { + } + var i = 0 +} + +var numbers = [42] + +struct S { +static let numbers = [42, 404, 101] +} + +@propertyWrapper +struct X { + var wrappedValue: T +} + +@propertyWrapper +struct Y { + var wrappedValue: Int +} + +struct Wrapped { + @X @Y var wrapped : Int +} diff --git a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl.expected b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl.expected new file mode 100644 index 00000000000..8f559769407 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl.expected @@ -0,0 +1,15 @@ +| param_decls.swift:1:10:1:13 | _ | getInterfaceType: | Int | getName: | _ | getType: | Int | isInout: | no | +| param_decls.swift:1:18:1:29 | y | getInterfaceType: | Double | getName: | y | getType: | Double | isInout: | yes | +| param_decls.swift:2:10:2:13 | _ | getInterfaceType: | Int | getName: | _ | getType: | Int | isInout: | no | +| param_decls.swift:2:18:2:29 | y | getInterfaceType: | Double | getName: | y | getType: | Double | isInout: | yes | +| param_decls.swift:5:5:5:5 | self | getInterfaceType: | S | getName: | self | getType: | S | isInout: | yes | +| param_decls.swift:5:15:5:15 | x | getInterfaceType: | Int | getName: | x | getType: | Int | isInout: | no | +| param_decls.swift:5:15:5:15 | x | getInterfaceType: | Int | getName: | x | getType: | Int | isInout: | no | +| param_decls.swift:5:15:5:18 | x | getInterfaceType: | Int | getName: | x | getType: | Int | isInout: | no | +| param_decls.swift:5:23:5:23 | y | getInterfaceType: | Int | getName: | y | getType: | Int | isInout: | no | +| param_decls.swift:5:23:5:23 | y | getInterfaceType: | Int | getName: | y | getType: | Int | isInout: | no | +| param_decls.swift:5:23:5:26 | y | getInterfaceType: | Int | getName: | y | getType: | Int | isInout: | no | +| param_decls.swift:7:9:7:9 | newValue | getInterfaceType: | Int? | getName: | newValue | getType: | Int? | isInout: | no | +| param_decls.swift:12:13:12:22 | s | getInterfaceType: | String | getName: | s | getType: | String | isInout: | yes | +| param_decls.swift:13:13:13:22 | s | getInterfaceType: | String | getName: | s | getType: | String | isInout: | yes | +| param_decls.swift:14:26:14:26 | $0 | getInterfaceType: | Int | getName: | $0 | getType: | Int | isInout: | no | diff --git a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl.ql b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl.ql new file mode 100644 index 00000000000..acf3614b7b0 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl.ql @@ -0,0 +1,14 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from ParamDecl x, Type getInterfaceType, string getName, Type getType, string isInout +where + toBeTested(x) and + not x.isUnknown() and + getInterfaceType = x.getInterfaceType() and + getName = x.getName() and + getType = x.getType() and + if x.isInout() then isInout = "yes" else isInout = "no" +select x, "getInterfaceType:", getInterfaceType, "getName:", getName, "getType:", getType, + "isInout:", isInout diff --git a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getAccessorDecl.expected b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getAccessorDecl.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getAccessorDecl.ql b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getAccessorDecl.ql new file mode 100644 index 00000000000..a751e0006cc --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getAccessorDecl.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from ParamDecl x, int index +where toBeTested(x) and not x.isUnknown() +select x, index, x.getAccessorDecl(index) diff --git a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getAttachedPropertyWrapperType.expected b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getAttachedPropertyWrapperType.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getAttachedPropertyWrapperType.ql b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getAttachedPropertyWrapperType.ql new file mode 100644 index 00000000000..0e71fb1cf26 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getAttachedPropertyWrapperType.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from ParamDecl x +where toBeTested(x) and not x.isUnknown() +select x, x.getAttachedPropertyWrapperType() diff --git a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getParentInitializer.expected b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getParentInitializer.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getParentInitializer.ql b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getParentInitializer.ql new file mode 100644 index 00000000000..39f7e9e1b6e --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getParentInitializer.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from ParamDecl x +where toBeTested(x) and not x.isUnknown() +select x, x.getParentInitializer() diff --git a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getParentPattern.expected b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getParentPattern.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getParentPattern.ql b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getParentPattern.ql new file mode 100644 index 00000000000..17b5a06c1d5 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getParentPattern.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from ParamDecl x +where toBeTested(x) and not x.isUnknown() +select x, x.getParentPattern() diff --git a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/param_decls.swift b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/param_decls.swift new file mode 100644 index 00000000000..72b47ed2c60 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/param_decls.swift @@ -0,0 +1,15 @@ +func foo(_: Int, x y: inout Double) {} +func bar(_: Int, x y: inout Double) {} + +struct S { + subscript(x: Int, y: Int) -> Int? { + get { nil } + set {} + } +} + +func closures() { + let x = {(s: inout String) -> String in s} + let y = {(s: inout String) -> String in ""} + let z : (Int) -> Int = { $0 + 1 } +} From 7d7966e71135785aa59fbd255a9df730121df482 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Thu, 28 Jul 2022 12:43:30 +0200 Subject: [PATCH 344/505] Swift: make trap key prefixes readable This replaces numeric tag-based prefixes with the actual tag name. While this means in general slightly larger trap files, it aids debugging them for a human. In the future we can make this conditional on some kind of trap debug option, but for the moment it does not seem detrimental. --- swift/codegen/generators/trapgen.py | 3 +-- swift/codegen/lib/cpp.py | 1 - swift/codegen/templates/trap_tags_h.mustache | 2 +- swift/codegen/test/test_cpp.py | 4 ++-- swift/codegen/test/test_trapgen.py | 20 ++++++++++---------- 5 files changed, 14 insertions(+), 16 deletions(-) diff --git a/swift/codegen/generators/trapgen.py b/swift/codegen/generators/trapgen.py index eb701b629ef..22c46e6e146 100755 --- a/swift/codegen/generators/trapgen.py +++ b/swift/codegen/generators/trapgen.py @@ -86,11 +86,10 @@ def generate(opts, renderer): renderer.render(cpp.TrapList(entries, opts.dbscheme), out / dir / "TrapEntries") tags = [] - for index, tag in enumerate(toposort_flatten(tag_graph)): + for tag in toposort_flatten(tag_graph): tags.append(cpp.Tag( name=get_tag_name(tag), bases=[get_tag_name(b) for b in sorted(tag_graph[tag])], - index=index, id=tag, )) renderer.render(cpp.TagList(tags, opts.dbscheme), out / "TrapTags") diff --git a/swift/codegen/lib/cpp.py b/swift/codegen/lib/cpp.py index 9eb5f78af58..3542efcdee9 100644 --- a/swift/codegen/lib/cpp.py +++ b/swift/codegen/lib/cpp.py @@ -83,7 +83,6 @@ class TagBase: class Tag: name: str bases: List[TagBase] - index: int id: str def __post_init__(self): diff --git a/swift/codegen/templates/trap_tags_h.mustache b/swift/codegen/templates/trap_tags_h.mustache index feac64ff92a..c444d28afcb 100644 --- a/swift/codegen/templates/trap_tags_h.mustache +++ b/swift/codegen/templates/trap_tags_h.mustache @@ -7,7 +7,7 @@ namespace codeql { // {{id}} struct {{name}}Tag {{#has_bases}}: {{#bases}}{{^first}}, {{/first}}{{base}}Tag{{/bases}} {{/has_bases}}{ - static constexpr const char* prefix = "{{index}}"; + static constexpr const char* prefix = "{{name}}"; }; {{/tags}} } diff --git a/swift/codegen/test/test_cpp.py b/swift/codegen/test/test_cpp.py index e6cd4f81305..d04877e38c4 100644 --- a/swift/codegen/test/test_cpp.py +++ b/swift/codegen/test/test_cpp.py @@ -65,7 +65,7 @@ def test_trap_has_first_field_marked(): def test_tag_has_first_base_marked(): bases = ["a", "b", "c"] expected = [cpp.TagBase("a", first=True), cpp.TagBase("b"), cpp.TagBase("c")] - t = cpp.Tag("name", bases, 0, "id") + t = cpp.Tag("name", bases, "id") assert t.bases == expected @@ -75,7 +75,7 @@ def test_tag_has_first_base_marked(): (["a", "b"], True) ]) def test_tag_has_bases(bases, expected): - t = cpp.Tag("name", bases, 0, "id") + t = cpp.Tag("name", bases, "id") assert t.has_bases is expected diff --git a/swift/codegen/test/test_trapgen.py b/swift/codegen/test/test_trapgen.py index 376f69c43a3..c8121ce48ba 100644 --- a/swift/codegen/test/test_trapgen.py +++ b/swift/codegen/test/test_trapgen.py @@ -162,10 +162,10 @@ def test_one_union_tags(generate_tags): assert generate_tags([ dbscheme.Union(lhs="@left_hand_side", rhs=["@b", "@a", "@c"]), ]) == [ - cpp.Tag(name="LeftHandSide", bases=[], index=0, id="@left_hand_side"), - cpp.Tag(name="A", bases=["LeftHandSide"], index=1, id="@a"), - cpp.Tag(name="B", bases=["LeftHandSide"], index=2, id="@b"), - cpp.Tag(name="C", bases=["LeftHandSide"], index=3, id="@c"), + cpp.Tag(name="LeftHandSide", bases=[], id="@left_hand_side"), + cpp.Tag(name="A", bases=["LeftHandSide"], id="@a"), + cpp.Tag(name="B", bases=["LeftHandSide"], id="@b"), + cpp.Tag(name="C", bases=["LeftHandSide"], id="@c"), ] @@ -175,12 +175,12 @@ def test_multiple_union_tags(generate_tags): dbscheme.Union(lhs="@a", rhs=["@b", "@c"]), dbscheme.Union(lhs="@e", rhs=["@c", "@f"]), ]) == [ - cpp.Tag(name="D", bases=[], index=0, id="@d"), - cpp.Tag(name="E", bases=[], index=1, id="@e"), - cpp.Tag(name="A", bases=["D"], index=2, id="@a"), - cpp.Tag(name="F", bases=["E"], index=3, id="@f"), - cpp.Tag(name="B", bases=["A"], index=4, id="@b"), - cpp.Tag(name="C", bases=["A", "E"], index=5, id="@c"), + cpp.Tag(name="D", bases=[], id="@d"), + cpp.Tag(name="E", bases=[], id="@e"), + cpp.Tag(name="A", bases=["D"], id="@a"), + cpp.Tag(name="F", bases=["E"], id="@f"), + cpp.Tag(name="B", bases=["A"], id="@b"), + cpp.Tag(name="C", bases=["A", "E"], id="@c"), ] From d547a417c93a8b26e66f985c71e64271e5fc97c0 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Thu, 28 Jul 2022 12:57:12 +0200 Subject: [PATCH 345/505] Swift: accept new test results --- .../posix-only/cross-references/VarDecls.expected | 1 - 1 file changed, 1 deletion(-) diff --git a/swift/integration-tests/posix-only/cross-references/VarDecls.expected b/swift/integration-tests/posix-only/cross-references/VarDecls.expected index 104271d5384..6fab086e7dd 100644 --- a/swift/integration-tests/posix-only/cross-references/VarDecls.expected +++ b/swift/integration-tests/posix-only/cross-references/VarDecls.expected @@ -1,5 +1,4 @@ | Sources/cross-references/lib.swift:10:5:10:5 | X | -| Sources/cross-references/lib.swift:10:5:10:5 | X | | Sources/cross-references/lib.swift:17:16:17:19 | v | | Sources/cross-references/lib.swift:22:16:22:19 | v | | Sources/cross-references/lib.swift:27:8:27:13 | lhs | From 212786ed91a14f2c4c9d80780af6187cf554e943 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 28 Jul 2022 13:38:35 +0000 Subject: [PATCH 346/505] Release preparation for version 2.10.2 --- cpp/ql/lib/CHANGELOG.md | 6 ++++++ .../0.3.2.md} | 7 ++++--- cpp/ql/lib/codeql-pack.release.yml | 2 +- cpp/ql/lib/qlpack.yml | 2 +- cpp/ql/src/CHANGELOG.md | 2 ++ cpp/ql/src/change-notes/released/0.3.1.md | 1 + cpp/ql/src/codeql-pack.release.yml | 2 +- cpp/ql/src/qlpack.yml | 2 +- csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md | 2 ++ .../Solorigate/lib/change-notes/released/1.2.2.md | 1 + .../campaigns/Solorigate/lib/codeql-pack.release.yml | 2 +- csharp/ql/campaigns/Solorigate/lib/qlpack.yml | 2 +- csharp/ql/campaigns/Solorigate/src/CHANGELOG.md | 2 ++ .../Solorigate/src/change-notes/released/1.2.2.md | 1 + .../campaigns/Solorigate/src/codeql-pack.release.yml | 2 +- csharp/ql/campaigns/Solorigate/src/qlpack.yml | 2 +- csharp/ql/lib/CHANGELOG.md | 2 ++ csharp/ql/lib/change-notes/released/0.3.2.md | 1 + csharp/ql/lib/codeql-pack.release.yml | 2 +- csharp/ql/lib/qlpack.yml | 2 +- csharp/ql/src/CHANGELOG.md | 2 ++ csharp/ql/src/change-notes/released/0.3.1.md | 1 + csharp/ql/src/codeql-pack.release.yml | 2 +- csharp/ql/src/qlpack.yml | 2 +- go/ql/lib/CHANGELOG.md | 2 ++ go/ql/lib/change-notes/released/0.2.2.md | 1 + go/ql/lib/codeql-pack.release.yml | 2 +- go/ql/lib/qlpack.yml | 2 +- go/ql/src/CHANGELOG.md | 2 ++ go/ql/src/change-notes/released/0.2.2.md | 1 + go/ql/src/codeql-pack.release.yml | 2 +- go/ql/src/qlpack.yml | 2 +- java/ql/lib/CHANGELOG.md | 12 ++++++++++++ .../change-notes/2022-05-12-get-underlying-expr.md | 4 ---- .../ql/lib/change-notes/2022-07-26-scanner-models.md | 4 ---- .../lib/change-notes/2022-07-27-nullness-junit5.md | 5 ----- java/ql/lib/change-notes/released/0.3.2.md | 11 +++++++++++ java/ql/lib/codeql-pack.release.yml | 2 +- java/ql/lib/qlpack.yml | 2 +- java/ql/src/CHANGELOG.md | 2 ++ java/ql/src/change-notes/released/0.3.1.md | 1 + java/ql/src/codeql-pack.release.yml | 2 +- java/ql/src/qlpack.yml | 2 +- javascript/ql/lib/CHANGELOG.md | 2 ++ javascript/ql/lib/change-notes/released/0.2.2.md | 1 + javascript/ql/lib/codeql-pack.release.yml | 2 +- javascript/ql/lib/qlpack.yml | 2 +- javascript/ql/src/CHANGELOG.md | 7 +++++++ .../0.3.1.md} | 6 +++--- javascript/ql/src/codeql-pack.release.yml | 2 +- javascript/ql/src/qlpack.yml | 2 +- python/ql/lib/CHANGELOG.md | 2 ++ python/ql/lib/change-notes/released/0.5.2.md | 1 + python/ql/lib/codeql-pack.release.yml | 2 +- python/ql/lib/qlpack.yml | 2 +- python/ql/src/CHANGELOG.md | 6 ++++++ .../0.4.0.md} | 8 ++++---- python/ql/src/codeql-pack.release.yml | 2 +- python/ql/src/qlpack.yml | 2 +- ruby/ql/lib/CHANGELOG.md | 7 +++++++ ...2-07-18-sqli-in-activerecord-relation-annotate.md | 5 ----- ruby/ql/lib/change-notes/2022-07-19-arel.md | 4 ---- ruby/ql/lib/change-notes/released/0.3.2.md | 6 ++++++ ruby/ql/lib/codeql-pack.release.yml | 2 +- ruby/ql/lib/qlpack.yml | 2 +- ruby/ql/src/CHANGELOG.md | 7 +++++++ .../src/change-notes/2022-07-21-check-http-verb.md | 4 ---- ruby/ql/src/change-notes/2022-07-21-weak-params.md | 4 ---- ruby/ql/src/change-notes/released/0.3.1.md | 6 ++++++ ruby/ql/src/codeql-pack.release.yml | 2 +- ruby/ql/src/qlpack.yml | 2 +- 71 files changed, 141 insertions(+), 72 deletions(-) rename cpp/ql/lib/change-notes/{2022-06-24-unique-variable.md => released/0.3.2.md} (92%) create mode 100644 cpp/ql/src/change-notes/released/0.3.1.md create mode 100644 csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.2.2.md create mode 100644 csharp/ql/campaigns/Solorigate/src/change-notes/released/1.2.2.md create mode 100644 csharp/ql/lib/change-notes/released/0.3.2.md create mode 100644 csharp/ql/src/change-notes/released/0.3.1.md create mode 100644 go/ql/lib/change-notes/released/0.2.2.md create mode 100644 go/ql/src/change-notes/released/0.2.2.md delete mode 100644 java/ql/lib/change-notes/2022-05-12-get-underlying-expr.md delete mode 100644 java/ql/lib/change-notes/2022-07-26-scanner-models.md delete mode 100644 java/ql/lib/change-notes/2022-07-27-nullness-junit5.md create mode 100644 java/ql/lib/change-notes/released/0.3.2.md create mode 100644 java/ql/src/change-notes/released/0.3.1.md create mode 100644 javascript/ql/lib/change-notes/released/0.2.2.md rename javascript/ql/src/change-notes/{2022-06-27-case-sensitive-middleware.md => released/0.3.1.md} (88%) create mode 100644 python/ql/lib/change-notes/released/0.5.2.md rename python/ql/src/change-notes/{2022-07-15-move-contextual-queries.md => released/0.4.0.md} (78%) delete mode 100644 ruby/ql/lib/change-notes/2022-07-18-sqli-in-activerecord-relation-annotate.md delete mode 100644 ruby/ql/lib/change-notes/2022-07-19-arel.md create mode 100644 ruby/ql/lib/change-notes/released/0.3.2.md delete mode 100644 ruby/ql/src/change-notes/2022-07-21-check-http-verb.md delete mode 100644 ruby/ql/src/change-notes/2022-07-21-weak-params.md create mode 100644 ruby/ql/src/change-notes/released/0.3.1.md diff --git a/cpp/ql/lib/CHANGELOG.md b/cpp/ql/lib/CHANGELOG.md index 75a047d6f64..9b4761ec2ce 100644 --- a/cpp/ql/lib/CHANGELOG.md +++ b/cpp/ql/lib/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.3.2 + +### Bug Fixes + +* Under certain circumstances a variable declaration that is not also a definition could be associated with a `Variable` that did not have the definition as a `VariableDeclarationEntry`. This is now fixed, and a unique `Variable` will exist that has both the declaration and the definition as a `VariableDeclarationEntry`. + ## 0.3.1 ### Minor Analysis Improvements diff --git a/cpp/ql/lib/change-notes/2022-06-24-unique-variable.md b/cpp/ql/lib/change-notes/released/0.3.2.md similarity index 92% rename from cpp/ql/lib/change-notes/2022-06-24-unique-variable.md rename to cpp/ql/lib/change-notes/released/0.3.2.md index e04dde1290a..9d3ca0cca67 100644 --- a/cpp/ql/lib/change-notes/2022-06-24-unique-variable.md +++ b/cpp/ql/lib/change-notes/released/0.3.2.md @@ -1,4 +1,5 @@ ---- -category: fix ---- +## 0.3.2 + +### Bug Fixes + * Under certain circumstances a variable declaration that is not also a definition could be associated with a `Variable` that did not have the definition as a `VariableDeclarationEntry`. This is now fixed, and a unique `Variable` will exist that has both the declaration and the definition as a `VariableDeclarationEntry`. diff --git a/cpp/ql/lib/codeql-pack.release.yml b/cpp/ql/lib/codeql-pack.release.yml index bb106b1cb63..18c64250f42 100644 --- a/cpp/ql/lib/codeql-pack.release.yml +++ b/cpp/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.3.1 +lastReleaseVersion: 0.3.2 diff --git a/cpp/ql/lib/qlpack.yml b/cpp/ql/lib/qlpack.yml index ce90251f83f..2761c28d94c 100644 --- a/cpp/ql/lib/qlpack.yml +++ b/cpp/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/cpp-all -version: 0.3.2-dev +version: 0.3.2 groups: cpp dbscheme: semmlecode.cpp.dbscheme extractor: cpp diff --git a/cpp/ql/src/CHANGELOG.md b/cpp/ql/src/CHANGELOG.md index e87fc5dce39..ae7e4f7151b 100644 --- a/cpp/ql/src/CHANGELOG.md +++ b/cpp/ql/src/CHANGELOG.md @@ -1,3 +1,5 @@ +## 0.3.1 + ## 0.3.0 ### Breaking Changes diff --git a/cpp/ql/src/change-notes/released/0.3.1.md b/cpp/ql/src/change-notes/released/0.3.1.md new file mode 100644 index 00000000000..2b0719929a1 --- /dev/null +++ b/cpp/ql/src/change-notes/released/0.3.1.md @@ -0,0 +1 @@ +## 0.3.1 diff --git a/cpp/ql/src/codeql-pack.release.yml b/cpp/ql/src/codeql-pack.release.yml index 95f6e3a0ba6..bb106b1cb63 100644 --- a/cpp/ql/src/codeql-pack.release.yml +++ b/cpp/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.3.0 +lastReleaseVersion: 0.3.1 diff --git a/cpp/ql/src/qlpack.yml b/cpp/ql/src/qlpack.yml index 2735b4d5289..b9902eb8bb4 100644 --- a/cpp/ql/src/qlpack.yml +++ b/cpp/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/cpp-queries -version: 0.3.1-dev +version: 0.3.1 groups: - cpp - queries diff --git a/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md b/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md index de0a7eeae4b..0efa6239b0f 100644 --- a/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md +++ b/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md @@ -1,3 +1,5 @@ +## 1.2.2 + ## 1.2.1 ## 1.2.0 diff --git a/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.2.2.md b/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.2.2.md new file mode 100644 index 00000000000..81af4d86d3b --- /dev/null +++ b/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.2.2.md @@ -0,0 +1 @@ +## 1.2.2 diff --git a/csharp/ql/campaigns/Solorigate/lib/codeql-pack.release.yml b/csharp/ql/campaigns/Solorigate/lib/codeql-pack.release.yml index 73dd403938c..0a70a9a01a7 100644 --- a/csharp/ql/campaigns/Solorigate/lib/codeql-pack.release.yml +++ b/csharp/ql/campaigns/Solorigate/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 1.2.1 +lastReleaseVersion: 1.2.2 diff --git a/csharp/ql/campaigns/Solorigate/lib/qlpack.yml b/csharp/ql/campaigns/Solorigate/lib/qlpack.yml index fc22389c2a8..08e6e1a8c82 100644 --- a/csharp/ql/campaigns/Solorigate/lib/qlpack.yml +++ b/csharp/ql/campaigns/Solorigate/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/csharp-solorigate-all -version: 1.2.2-dev +version: 1.2.2 groups: - csharp - solorigate diff --git a/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md b/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md index de0a7eeae4b..0efa6239b0f 100644 --- a/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md +++ b/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md @@ -1,3 +1,5 @@ +## 1.2.2 + ## 1.2.1 ## 1.2.0 diff --git a/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.2.2.md b/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.2.2.md new file mode 100644 index 00000000000..81af4d86d3b --- /dev/null +++ b/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.2.2.md @@ -0,0 +1 @@ +## 1.2.2 diff --git a/csharp/ql/campaigns/Solorigate/src/codeql-pack.release.yml b/csharp/ql/campaigns/Solorigate/src/codeql-pack.release.yml index 73dd403938c..0a70a9a01a7 100644 --- a/csharp/ql/campaigns/Solorigate/src/codeql-pack.release.yml +++ b/csharp/ql/campaigns/Solorigate/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 1.2.1 +lastReleaseVersion: 1.2.2 diff --git a/csharp/ql/campaigns/Solorigate/src/qlpack.yml b/csharp/ql/campaigns/Solorigate/src/qlpack.yml index a2ef81cc0e4..89620dec618 100644 --- a/csharp/ql/campaigns/Solorigate/src/qlpack.yml +++ b/csharp/ql/campaigns/Solorigate/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/csharp-solorigate-queries -version: 1.2.2-dev +version: 1.2.2 groups: - csharp - solorigate diff --git a/csharp/ql/lib/CHANGELOG.md b/csharp/ql/lib/CHANGELOG.md index d1c89626798..5ea16d73e48 100644 --- a/csharp/ql/lib/CHANGELOG.md +++ b/csharp/ql/lib/CHANGELOG.md @@ -1,3 +1,5 @@ +## 0.3.2 + ## 0.3.1 ## 0.3.0 diff --git a/csharp/ql/lib/change-notes/released/0.3.2.md b/csharp/ql/lib/change-notes/released/0.3.2.md new file mode 100644 index 00000000000..8309e697333 --- /dev/null +++ b/csharp/ql/lib/change-notes/released/0.3.2.md @@ -0,0 +1 @@ +## 0.3.2 diff --git a/csharp/ql/lib/codeql-pack.release.yml b/csharp/ql/lib/codeql-pack.release.yml index bb106b1cb63..18c64250f42 100644 --- a/csharp/ql/lib/codeql-pack.release.yml +++ b/csharp/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.3.1 +lastReleaseVersion: 0.3.2 diff --git a/csharp/ql/lib/qlpack.yml b/csharp/ql/lib/qlpack.yml index 0d72cfc0c65..d1409a61b13 100644 --- a/csharp/ql/lib/qlpack.yml +++ b/csharp/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/csharp-all -version: 0.3.2-dev +version: 0.3.2 groups: csharp dbscheme: semmlecode.csharp.dbscheme extractor: csharp diff --git a/csharp/ql/src/CHANGELOG.md b/csharp/ql/src/CHANGELOG.md index bf9e8f9c41f..bb530ba1727 100644 --- a/csharp/ql/src/CHANGELOG.md +++ b/csharp/ql/src/CHANGELOG.md @@ -1,3 +1,5 @@ +## 0.3.1 + ## 0.3.0 ### Breaking Changes diff --git a/csharp/ql/src/change-notes/released/0.3.1.md b/csharp/ql/src/change-notes/released/0.3.1.md new file mode 100644 index 00000000000..2b0719929a1 --- /dev/null +++ b/csharp/ql/src/change-notes/released/0.3.1.md @@ -0,0 +1 @@ +## 0.3.1 diff --git a/csharp/ql/src/codeql-pack.release.yml b/csharp/ql/src/codeql-pack.release.yml index 95f6e3a0ba6..bb106b1cb63 100644 --- a/csharp/ql/src/codeql-pack.release.yml +++ b/csharp/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.3.0 +lastReleaseVersion: 0.3.1 diff --git a/csharp/ql/src/qlpack.yml b/csharp/ql/src/qlpack.yml index d3ceb328420..c3e1381bf55 100644 --- a/csharp/ql/src/qlpack.yml +++ b/csharp/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/csharp-queries -version: 0.3.1-dev +version: 0.3.1 groups: - csharp - queries diff --git a/go/ql/lib/CHANGELOG.md b/go/ql/lib/CHANGELOG.md index 23c4fc2eb4f..a4ead0ef794 100644 --- a/go/ql/lib/CHANGELOG.md +++ b/go/ql/lib/CHANGELOG.md @@ -1,3 +1,5 @@ +## 0.2.2 + ## 0.2.1 ## 0.2.0 diff --git a/go/ql/lib/change-notes/released/0.2.2.md b/go/ql/lib/change-notes/released/0.2.2.md new file mode 100644 index 00000000000..fc31cbd3d6f --- /dev/null +++ b/go/ql/lib/change-notes/released/0.2.2.md @@ -0,0 +1 @@ +## 0.2.2 diff --git a/go/ql/lib/codeql-pack.release.yml b/go/ql/lib/codeql-pack.release.yml index df29a726bcc..16a06790aa8 100644 --- a/go/ql/lib/codeql-pack.release.yml +++ b/go/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.2.1 +lastReleaseVersion: 0.2.2 diff --git a/go/ql/lib/qlpack.yml b/go/ql/lib/qlpack.yml index c360e550193..200393fbd6c 100644 --- a/go/ql/lib/qlpack.yml +++ b/go/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/go-all -version: 0.2.2-dev +version: 0.2.2 groups: go dbscheme: go.dbscheme extractor: go diff --git a/go/ql/src/CHANGELOG.md b/go/ql/src/CHANGELOG.md index 1697aa9e561..c981e074fad 100644 --- a/go/ql/src/CHANGELOG.md +++ b/go/ql/src/CHANGELOG.md @@ -1,3 +1,5 @@ +## 0.2.2 + ## 0.2.1 ## 0.2.0 diff --git a/go/ql/src/change-notes/released/0.2.2.md b/go/ql/src/change-notes/released/0.2.2.md new file mode 100644 index 00000000000..fc31cbd3d6f --- /dev/null +++ b/go/ql/src/change-notes/released/0.2.2.md @@ -0,0 +1 @@ +## 0.2.2 diff --git a/go/ql/src/codeql-pack.release.yml b/go/ql/src/codeql-pack.release.yml index df29a726bcc..16a06790aa8 100644 --- a/go/ql/src/codeql-pack.release.yml +++ b/go/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.2.1 +lastReleaseVersion: 0.2.2 diff --git a/go/ql/src/qlpack.yml b/go/ql/src/qlpack.yml index 75ed3c98275..df3aa78b2cf 100644 --- a/go/ql/src/qlpack.yml +++ b/go/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/go-queries -version: 0.2.2-dev +version: 0.2.2 groups: - go - queries diff --git a/java/ql/lib/CHANGELOG.md b/java/ql/lib/CHANGELOG.md index b5ceb823e75..49ad072ce54 100644 --- a/java/ql/lib/CHANGELOG.md +++ b/java/ql/lib/CHANGELOG.md @@ -1,3 +1,15 @@ +## 0.3.2 + +### New Features + +* The QL predicate `Expr::getUnderlyingExpr` has been added. It can be used to look through casts and not-null expressions and obtain the underlying expression to which they apply. + +### Minor Analysis Improvements + +* The JUnit5 version of `AssertNotNull` is now recognized, which removes + related false positives in the nullness queries. +* Added data flow models for `java.util.Scanner`. + ## 0.3.1 ### New Features diff --git a/java/ql/lib/change-notes/2022-05-12-get-underlying-expr.md b/java/ql/lib/change-notes/2022-05-12-get-underlying-expr.md deleted file mode 100644 index f24c9379abb..00000000000 --- a/java/ql/lib/change-notes/2022-05-12-get-underlying-expr.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: feature ---- -* The QL predicate `Expr::getUnderlyingExpr` has been added. It can be used to look through casts and not-null expressions and obtain the underlying expression to which they apply. diff --git a/java/ql/lib/change-notes/2022-07-26-scanner-models.md b/java/ql/lib/change-notes/2022-07-26-scanner-models.md deleted file mode 100644 index 6a78982d639..00000000000 --- a/java/ql/lib/change-notes/2022-07-26-scanner-models.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* Added data flow models for `java.util.Scanner`. \ No newline at end of file diff --git a/java/ql/lib/change-notes/2022-07-27-nullness-junit5.md b/java/ql/lib/change-notes/2022-07-27-nullness-junit5.md deleted file mode 100644 index 6cfb0949c69..00000000000 --- a/java/ql/lib/change-notes/2022-07-27-nullness-junit5.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -category: minorAnalysis ---- -* The JUnit5 version of `AssertNotNull` is now recognized, which removes - related false positives in the nullness queries. diff --git a/java/ql/lib/change-notes/released/0.3.2.md b/java/ql/lib/change-notes/released/0.3.2.md new file mode 100644 index 00000000000..cf49b858e8f --- /dev/null +++ b/java/ql/lib/change-notes/released/0.3.2.md @@ -0,0 +1,11 @@ +## 0.3.2 + +### New Features + +* The QL predicate `Expr::getUnderlyingExpr` has been added. It can be used to look through casts and not-null expressions and obtain the underlying expression to which they apply. + +### Minor Analysis Improvements + +* The JUnit5 version of `AssertNotNull` is now recognized, which removes + related false positives in the nullness queries. +* Added data flow models for `java.util.Scanner`. diff --git a/java/ql/lib/codeql-pack.release.yml b/java/ql/lib/codeql-pack.release.yml index bb106b1cb63..18c64250f42 100644 --- a/java/ql/lib/codeql-pack.release.yml +++ b/java/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.3.1 +lastReleaseVersion: 0.3.2 diff --git a/java/ql/lib/qlpack.yml b/java/ql/lib/qlpack.yml index 0de218dcd22..261f0508c36 100644 --- a/java/ql/lib/qlpack.yml +++ b/java/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/java-all -version: 0.3.2-dev +version: 0.3.2 groups: java dbscheme: config/semmlecode.dbscheme extractor: java diff --git a/java/ql/src/CHANGELOG.md b/java/ql/src/CHANGELOG.md index b39e648bf04..33ae45fbb9f 100644 --- a/java/ql/src/CHANGELOG.md +++ b/java/ql/src/CHANGELOG.md @@ -1,3 +1,5 @@ +## 0.3.1 + ## 0.3.0 ### Breaking Changes diff --git a/java/ql/src/change-notes/released/0.3.1.md b/java/ql/src/change-notes/released/0.3.1.md new file mode 100644 index 00000000000..2b0719929a1 --- /dev/null +++ b/java/ql/src/change-notes/released/0.3.1.md @@ -0,0 +1 @@ +## 0.3.1 diff --git a/java/ql/src/codeql-pack.release.yml b/java/ql/src/codeql-pack.release.yml index 95f6e3a0ba6..bb106b1cb63 100644 --- a/java/ql/src/codeql-pack.release.yml +++ b/java/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.3.0 +lastReleaseVersion: 0.3.1 diff --git a/java/ql/src/qlpack.yml b/java/ql/src/qlpack.yml index 9cd3341f443..87c9e78e07f 100644 --- a/java/ql/src/qlpack.yml +++ b/java/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/java-queries -version: 0.3.1-dev +version: 0.3.1 groups: - java - queries diff --git a/javascript/ql/lib/CHANGELOG.md b/javascript/ql/lib/CHANGELOG.md index 23d54f955a7..6f359e0ac85 100644 --- a/javascript/ql/lib/CHANGELOG.md +++ b/javascript/ql/lib/CHANGELOG.md @@ -1,3 +1,5 @@ +## 0.2.2 + ## 0.2.1 ### Minor Analysis Improvements diff --git a/javascript/ql/lib/change-notes/released/0.2.2.md b/javascript/ql/lib/change-notes/released/0.2.2.md new file mode 100644 index 00000000000..fc31cbd3d6f --- /dev/null +++ b/javascript/ql/lib/change-notes/released/0.2.2.md @@ -0,0 +1 @@ +## 0.2.2 diff --git a/javascript/ql/lib/codeql-pack.release.yml b/javascript/ql/lib/codeql-pack.release.yml index df29a726bcc..16a06790aa8 100644 --- a/javascript/ql/lib/codeql-pack.release.yml +++ b/javascript/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.2.1 +lastReleaseVersion: 0.2.2 diff --git a/javascript/ql/lib/qlpack.yml b/javascript/ql/lib/qlpack.yml index 9a05a09e0b6..c1449f8acce 100644 --- a/javascript/ql/lib/qlpack.yml +++ b/javascript/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/javascript-all -version: 0.2.2-dev +version: 0.2.2 groups: javascript dbscheme: semmlecode.javascript.dbscheme extractor: javascript diff --git a/javascript/ql/src/CHANGELOG.md b/javascript/ql/src/CHANGELOG.md index baf7f9b85e0..00016a45458 100644 --- a/javascript/ql/src/CHANGELOG.md +++ b/javascript/ql/src/CHANGELOG.md @@ -1,3 +1,10 @@ +## 0.3.1 + +### New Queries + +- A new query "Case-sensitive middleware path" (`js/case-sensitive-middleware-path`) has been added. + It highlights middleware routes that can be bypassed due to having a case-sensitive regular expression path. + ## 0.3.0 ### Breaking Changes diff --git a/javascript/ql/src/change-notes/2022-06-27-case-sensitive-middleware.md b/javascript/ql/src/change-notes/released/0.3.1.md similarity index 88% rename from javascript/ql/src/change-notes/2022-06-27-case-sensitive-middleware.md rename to javascript/ql/src/change-notes/released/0.3.1.md index 09895db1e2c..8fe1aaaf4ef 100644 --- a/javascript/ql/src/change-notes/2022-06-27-case-sensitive-middleware.md +++ b/javascript/ql/src/change-notes/released/0.3.1.md @@ -1,6 +1,6 @@ ---- -category: newQuery ---- +## 0.3.1 + +### New Queries - A new query "Case-sensitive middleware path" (`js/case-sensitive-middleware-path`) has been added. It highlights middleware routes that can be bypassed due to having a case-sensitive regular expression path. diff --git a/javascript/ql/src/codeql-pack.release.yml b/javascript/ql/src/codeql-pack.release.yml index 95f6e3a0ba6..bb106b1cb63 100644 --- a/javascript/ql/src/codeql-pack.release.yml +++ b/javascript/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.3.0 +lastReleaseVersion: 0.3.1 diff --git a/javascript/ql/src/qlpack.yml b/javascript/ql/src/qlpack.yml index 5525fe8b54b..72dda406008 100644 --- a/javascript/ql/src/qlpack.yml +++ b/javascript/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/javascript-queries -version: 0.3.1-dev +version: 0.3.1 groups: - javascript - queries diff --git a/python/ql/lib/CHANGELOG.md b/python/ql/lib/CHANGELOG.md index 83a09c70446..b57f612b336 100644 --- a/python/ql/lib/CHANGELOG.md +++ b/python/ql/lib/CHANGELOG.md @@ -1,3 +1,5 @@ +## 0.5.2 + ## 0.5.1 ### Deprecated APIs diff --git a/python/ql/lib/change-notes/released/0.5.2.md b/python/ql/lib/change-notes/released/0.5.2.md new file mode 100644 index 00000000000..33ae68a2827 --- /dev/null +++ b/python/ql/lib/change-notes/released/0.5.2.md @@ -0,0 +1 @@ +## 0.5.2 diff --git a/python/ql/lib/codeql-pack.release.yml b/python/ql/lib/codeql-pack.release.yml index 0bf7024c337..2d9d3f587f8 100644 --- a/python/ql/lib/codeql-pack.release.yml +++ b/python/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.5.1 +lastReleaseVersion: 0.5.2 diff --git a/python/ql/lib/qlpack.yml b/python/ql/lib/qlpack.yml index f1a7c716b1e..5cd0847d929 100644 --- a/python/ql/lib/qlpack.yml +++ b/python/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/python-all -version: 0.5.2-dev +version: 0.5.2 groups: python dbscheme: semmlecode.python.dbscheme extractor: python diff --git a/python/ql/src/CHANGELOG.md b/python/ql/src/CHANGELOG.md index fae4ab0dc9a..8fdacb47f64 100644 --- a/python/ql/src/CHANGELOG.md +++ b/python/ql/src/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.4.0 + +### Breaking Changes + +* Contextual queries and the query libraries they depend on have been moved to the `codeql/python-all` package. + ## 0.3.0 ### Breaking Changes diff --git a/python/ql/src/change-notes/2022-07-15-move-contextual-queries.md b/python/ql/src/change-notes/released/0.4.0.md similarity index 78% rename from python/ql/src/change-notes/2022-07-15-move-contextual-queries.md rename to python/ql/src/change-notes/released/0.4.0.md index 25ae1b57b99..c6658b7780f 100644 --- a/python/ql/src/change-notes/2022-07-15-move-contextual-queries.md +++ b/python/ql/src/change-notes/released/0.4.0.md @@ -1,5 +1,5 @@ ---- -category: breaking ---- -* Contextual queries and the query libraries they depend on have been moved to the `codeql/python-all` package. +## 0.4.0 +### Breaking Changes + +* Contextual queries and the query libraries they depend on have been moved to the `codeql/python-all` package. diff --git a/python/ql/src/codeql-pack.release.yml b/python/ql/src/codeql-pack.release.yml index 95f6e3a0ba6..458bfbeccff 100644 --- a/python/ql/src/codeql-pack.release.yml +++ b/python/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.3.0 +lastReleaseVersion: 0.4.0 diff --git a/python/ql/src/qlpack.yml b/python/ql/src/qlpack.yml index 155e57024e8..c70cb344e92 100644 --- a/python/ql/src/qlpack.yml +++ b/python/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/python-queries -version: 0.3.1-dev +version: 0.4.0 groups: - python - queries diff --git a/ruby/ql/lib/CHANGELOG.md b/ruby/ql/lib/CHANGELOG.md index fe8a12aa938..ae943f45599 100644 --- a/ruby/ql/lib/CHANGELOG.md +++ b/ruby/ql/lib/CHANGELOG.md @@ -1,3 +1,10 @@ +## 0.3.2 + +### Minor Analysis Improvements + +* Calls to `Arel.sql` are now recognised as propagating taint from their argument. +- Calls to `ActiveRecord::Relation#annotate` are now recognized as`SqlExecution`s so that it will be considered as a sink for queries like rb/sql-injection. + ## 0.3.1 ### Minor Analysis Improvements diff --git a/ruby/ql/lib/change-notes/2022-07-18-sqli-in-activerecord-relation-annotate.md b/ruby/ql/lib/change-notes/2022-07-18-sqli-in-activerecord-relation-annotate.md deleted file mode 100644 index 60ab137f8b2..00000000000 --- a/ruby/ql/lib/change-notes/2022-07-18-sqli-in-activerecord-relation-annotate.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -category: minorAnalysis ---- - -- Calls to `ActiveRecord::Relation#annotate` are now recognized as`SqlExecution`s so that it will be considered as a sink for queries like rb/sql-injection. \ No newline at end of file diff --git a/ruby/ql/lib/change-notes/2022-07-19-arel.md b/ruby/ql/lib/change-notes/2022-07-19-arel.md deleted file mode 100644 index 3dda3d4b1f6..00000000000 --- a/ruby/ql/lib/change-notes/2022-07-19-arel.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* Calls to `Arel.sql` are now recognised as propagating taint from their argument. diff --git a/ruby/ql/lib/change-notes/released/0.3.2.md b/ruby/ql/lib/change-notes/released/0.3.2.md new file mode 100644 index 00000000000..3e5710af675 --- /dev/null +++ b/ruby/ql/lib/change-notes/released/0.3.2.md @@ -0,0 +1,6 @@ +## 0.3.2 + +### Minor Analysis Improvements + +* Calls to `Arel.sql` are now recognised as propagating taint from their argument. +- Calls to `ActiveRecord::Relation#annotate` are now recognized as`SqlExecution`s so that it will be considered as a sink for queries like rb/sql-injection. diff --git a/ruby/ql/lib/codeql-pack.release.yml b/ruby/ql/lib/codeql-pack.release.yml index bb106b1cb63..18c64250f42 100644 --- a/ruby/ql/lib/codeql-pack.release.yml +++ b/ruby/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.3.1 +lastReleaseVersion: 0.3.2 diff --git a/ruby/ql/lib/qlpack.yml b/ruby/ql/lib/qlpack.yml index 8216fedd9d2..6cf140325c0 100644 --- a/ruby/ql/lib/qlpack.yml +++ b/ruby/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/ruby-all -version: 0.3.2-dev +version: 0.3.2 groups: ruby extractor: ruby dbscheme: ruby.dbscheme diff --git a/ruby/ql/src/CHANGELOG.md b/ruby/ql/src/CHANGELOG.md index 9f227fdc843..9aeec45dc3f 100644 --- a/ruby/ql/src/CHANGELOG.md +++ b/ruby/ql/src/CHANGELOG.md @@ -1,3 +1,10 @@ +## 0.3.1 + +### New Queries + +* Added a new experimental query, `rb/manually-checking-http-verb`, to detect cases when the HTTP verb for an incoming request is checked and then used as part of control flow. +* Added a new experimental query, `rb/weak-params`, to detect cases when the rails strong parameters pattern isn't followed and values flow into persistent store writes. + ## 0.3.0 ### Breaking Changes diff --git a/ruby/ql/src/change-notes/2022-07-21-check-http-verb.md b/ruby/ql/src/change-notes/2022-07-21-check-http-verb.md deleted file mode 100644 index 4a670ba1092..00000000000 --- a/ruby/ql/src/change-notes/2022-07-21-check-http-verb.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: newQuery ---- -* Added a new experimental query, `rb/manually-checking-http-verb`, to detect cases when the HTTP verb for an incoming request is checked and then used as part of control flow. \ No newline at end of file diff --git a/ruby/ql/src/change-notes/2022-07-21-weak-params.md b/ruby/ql/src/change-notes/2022-07-21-weak-params.md deleted file mode 100644 index 08b8f153989..00000000000 --- a/ruby/ql/src/change-notes/2022-07-21-weak-params.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: newQuery ---- -* Added a new experimental query, `rb/weak-params`, to detect cases when the rails strong parameters pattern isn't followed and values flow into persistent store writes. \ No newline at end of file diff --git a/ruby/ql/src/change-notes/released/0.3.1.md b/ruby/ql/src/change-notes/released/0.3.1.md new file mode 100644 index 00000000000..a95409eabd1 --- /dev/null +++ b/ruby/ql/src/change-notes/released/0.3.1.md @@ -0,0 +1,6 @@ +## 0.3.1 + +### New Queries + +* Added a new experimental query, `rb/manually-checking-http-verb`, to detect cases when the HTTP verb for an incoming request is checked and then used as part of control flow. +* Added a new experimental query, `rb/weak-params`, to detect cases when the rails strong parameters pattern isn't followed and values flow into persistent store writes. diff --git a/ruby/ql/src/codeql-pack.release.yml b/ruby/ql/src/codeql-pack.release.yml index 95f6e3a0ba6..bb106b1cb63 100644 --- a/ruby/ql/src/codeql-pack.release.yml +++ b/ruby/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.3.0 +lastReleaseVersion: 0.3.1 diff --git a/ruby/ql/src/qlpack.yml b/ruby/ql/src/qlpack.yml index 6715fc61912..17eb743f26d 100644 --- a/ruby/ql/src/qlpack.yml +++ b/ruby/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/ruby-queries -version: 0.3.1-dev +version: 0.3.1 groups: - ruby - queries From 3137addfc1be511218016dc125fb588e211c7789 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema <93738568+jketema@users.noreply.github.com> Date: Thu, 28 Jul 2022 15:44:53 +0200 Subject: [PATCH 347/505] Update ruby/ql/lib/CHANGELOG.md --- ruby/ql/lib/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruby/ql/lib/CHANGELOG.md b/ruby/ql/lib/CHANGELOG.md index ae943f45599..09d016efb49 100644 --- a/ruby/ql/lib/CHANGELOG.md +++ b/ruby/ql/lib/CHANGELOG.md @@ -3,7 +3,7 @@ ### Minor Analysis Improvements * Calls to `Arel.sql` are now recognised as propagating taint from their argument. -- Calls to `ActiveRecord::Relation#annotate` are now recognized as`SqlExecution`s so that it will be considered as a sink for queries like rb/sql-injection. +* Calls to `ActiveRecord::Relation#annotate` are now recognized as `SqlExecution`s so that it will be considered as a sink for queries like rb/sql-injection. ## 0.3.1 From 15a979cfc6cd940a59a5294367a0e036e8db7834 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema <93738568+jketema@users.noreply.github.com> Date: Thu, 28 Jul 2022 15:45:01 +0200 Subject: [PATCH 348/505] Update ruby/ql/lib/change-notes/released/0.3.2.md --- ruby/ql/lib/change-notes/released/0.3.2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruby/ql/lib/change-notes/released/0.3.2.md b/ruby/ql/lib/change-notes/released/0.3.2.md index 3e5710af675..bdb97f6d3ce 100644 --- a/ruby/ql/lib/change-notes/released/0.3.2.md +++ b/ruby/ql/lib/change-notes/released/0.3.2.md @@ -3,4 +3,4 @@ ### Minor Analysis Improvements * Calls to `Arel.sql` are now recognised as propagating taint from their argument. -- Calls to `ActiveRecord::Relation#annotate` are now recognized as`SqlExecution`s so that it will be considered as a sink for queries like rb/sql-injection. +* Calls to `ActiveRecord::Relation#annotate` are now recognized as `SqlExecution`s so that it will be considered as a sink for queries like rb/sql-injection. From 258b58cd37787b3d545d1b481af3eb387f6cb793 Mon Sep 17 00:00:00 2001 From: Alex Ford Date: Thu, 28 Jul 2022 14:58:34 +0100 Subject: [PATCH 349/505] Update java/ql/lib/CHANGELOG.md --- java/ql/lib/CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/java/ql/lib/CHANGELOG.md b/java/ql/lib/CHANGELOG.md index 49ad072ce54..150a12f68fe 100644 --- a/java/ql/lib/CHANGELOG.md +++ b/java/ql/lib/CHANGELOG.md @@ -6,8 +6,7 @@ ### Minor Analysis Improvements -* The JUnit5 version of `AssertNotNull` is now recognized, which removes - related false positives in the nullness queries. +* The JUnit5 version of `AssertNotNull` is now recognized, which removes related false positives in the nullness queries. * Added data flow models for `java.util.Scanner`. ## 0.3.1 From a8345e00fcf083b721606bf84869c33f79506ccb Mon Sep 17 00:00:00 2001 From: Alex Ford Date: Thu, 28 Jul 2022 14:58:38 +0100 Subject: [PATCH 350/505] Update java/ql/lib/change-notes/released/0.3.2.md --- java/ql/lib/change-notes/released/0.3.2.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/java/ql/lib/change-notes/released/0.3.2.md b/java/ql/lib/change-notes/released/0.3.2.md index cf49b858e8f..b1d193b28b5 100644 --- a/java/ql/lib/change-notes/released/0.3.2.md +++ b/java/ql/lib/change-notes/released/0.3.2.md @@ -6,6 +6,5 @@ ### Minor Analysis Improvements -* The JUnit5 version of `AssertNotNull` is now recognized, which removes - related false positives in the nullness queries. +* The JUnit5 version of `AssertNotNull` is now recognized, which removes related false positives in the nullness queries. * Added data flow models for `java.util.Scanner`. From e7f275382e7e152b965467e7d405a018cec43765 Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Thu, 14 Jul 2022 11:14:27 +0100 Subject: [PATCH 351/505] Add test for Java wildcard substitution --- .../wildcard-substitution/Lib.java | 17 ++++++ .../wildcard-substitution/User.java | 12 +++++ .../wildcard-substitution/test.expected | 54 +++++++++++++++++++ .../wildcard-substitution/test.ql | 7 +++ 4 files changed, 90 insertions(+) create mode 100644 java/ql/test/library-tests/wildcard-substitution/Lib.java create mode 100644 java/ql/test/library-tests/wildcard-substitution/User.java create mode 100644 java/ql/test/library-tests/wildcard-substitution/test.expected create mode 100644 java/ql/test/library-tests/wildcard-substitution/test.ql diff --git a/java/ql/test/library-tests/wildcard-substitution/Lib.java b/java/ql/test/library-tests/wildcard-substitution/Lib.java new file mode 100644 index 00000000000..9bd84a9218c --- /dev/null +++ b/java/ql/test/library-tests/wildcard-substitution/Lib.java @@ -0,0 +1,17 @@ +import java.util.List; + +public class Lib { + + public void takesVar(T t) { } + public void takesInvar(List lt) { } + public void takesUnbound(List lt) { } + public void takesExtends(List lt) { } + public void takesSuper(List lt) { } + + public T returnsVar() { return null; } + public List returnsInvar() { return null; } + public List returnsUnbound() { return null; } + public List returnsExtends() { return null; } + public List returnsSuper() { return null; } + +} diff --git a/java/ql/test/library-tests/wildcard-substitution/User.java b/java/ql/test/library-tests/wildcard-substitution/User.java new file mode 100644 index 00000000000..5f4b7fac21a --- /dev/null +++ b/java/ql/test/library-tests/wildcard-substitution/User.java @@ -0,0 +1,12 @@ +public class User { + + public static void test(Lib invarLib, Lib extendsLib, Lib superLib, Lib unboundLib) { + + invarLib.takesVar(null); + extendsLib.takesVar(null); + superLib.takesVar(null); + unboundLib.takesVar(null); + + } + +} diff --git a/java/ql/test/library-tests/wildcard-substitution/test.expected b/java/ql/test/library-tests/wildcard-substitution/test.expected new file mode 100644 index 00000000000..85af393c21e --- /dev/null +++ b/java/ql/test/library-tests/wildcard-substitution/test.expected @@ -0,0 +1,54 @@ +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsExtends | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsInvar | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsSuper | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsUnbound | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsVar | CharSequence | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesExtends | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesInvar | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesSuper | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesUnbound | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesVar | | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsExtends | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsInvar | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsSuper | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsUnbound | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsVar | Object | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesExtends | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesInvar | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesSuper | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesUnbound | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesVar | CharSequence | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsExtends | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsInvar | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsSuper | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsUnbound | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsVar | Object | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesExtends | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesInvar | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesSuper | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesUnbound | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesVar | | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsExtends | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsInvar | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsSuper | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsUnbound | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsVar | CharSequence | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesExtends | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesInvar | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesSuper | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesUnbound | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesVar | CharSequence | +| Lib.java:3:14:3:16 | Lib | Lib.java:5:15:5:22 | takesVar | T | +| Lib.java:3:14:3:16 | Lib | Lib.java:6:15:6:24 | takesInvar | List | +| Lib.java:3:14:3:16 | Lib | Lib.java:7:15:7:26 | takesUnbound | List | +| Lib.java:3:14:3:16 | Lib | Lib.java:8:15:8:26 | takesExtends | List | +| Lib.java:3:14:3:16 | Lib | Lib.java:9:15:9:24 | takesSuper | List | +| Lib.java:3:14:3:16 | Lib | Lib.java:11:12:11:21 | returnsVar | T | +| Lib.java:3:14:3:16 | Lib | Lib.java:12:18:12:29 | returnsInvar | List | +| Lib.java:3:14:3:16 | Lib | Lib.java:13:18:13:31 | returnsUnbound | List | +| Lib.java:3:14:3:16 | Lib | Lib.java:14:28:14:41 | returnsExtends | List | +| Lib.java:3:14:3:16 | Lib | Lib.java:15:26:15:37 | returnsSuper | List | +| User.java:1:14:1:17 | User | User.java:3:22:3:25 | test | Lib | +| User.java:1:14:1:17 | User | User.java:3:22:3:25 | test | Lib | +| User.java:1:14:1:17 | User | User.java:3:22:3:25 | test | Lib | +| User.java:1:14:1:17 | User | User.java:3:22:3:25 | test | Lib | diff --git a/java/ql/test/library-tests/wildcard-substitution/test.ql b/java/ql/test/library-tests/wildcard-substitution/test.ql new file mode 100644 index 00000000000..3fdf1c00a55 --- /dev/null +++ b/java/ql/test/library-tests/wildcard-substitution/test.ql @@ -0,0 +1,7 @@ +import java + +Type notVoid(Type t) { result = t and not result instanceof VoidType } + +from Callable c +where c.getSourceDeclaration().fromSource() +select c.getDeclaringType(), c, notVoid([c.getAParamType(), c.getReturnType()]).toString() From 7475f84ea5a69c5572e3cd0b954cb2c8ca9fdbc4 Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Thu, 14 Jul 2022 15:19:05 +0100 Subject: [PATCH 352/505] Fix type-parameter-out-of-scope test --- java/ql/consistency-queries/typeParametersInScope.ql | 2 ++ 1 file changed, 2 insertions(+) diff --git a/java/ql/consistency-queries/typeParametersInScope.ql b/java/ql/consistency-queries/typeParametersInScope.ql index f78bf2d42a4..2f1fd651278 100644 --- a/java/ql/consistency-queries/typeParametersInScope.ql +++ b/java/ql/consistency-queries/typeParametersInScope.ql @@ -12,6 +12,8 @@ Type getAMentionedType(RefType type) { result = getAMentionedType(type).(InstantiatedType).getATypeArgument() or result = getAMentionedType(type).(NestedType).getEnclosingType() + or + result = getAMentionedType(type).(Wildcard).getATypeBound().getType() } Type getATypeUsedInClass(RefType type) { From 8cd2aeb65dbc9768dda5056478dcd871df6df0f6 Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Thu, 14 Jul 2022 15:19:33 +0100 Subject: [PATCH 353/505] Accept test changes --- .../generic-instance-methods/test.expected | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/java/ql/test/kotlin/library-tests/generic-instance-methods/test.expected b/java/ql/test/kotlin/library-tests/generic-instance-methods/test.expected index 13c5c19bd68..b80b1311a64 100644 --- a/java/ql/test/kotlin/library-tests/generic-instance-methods/test.expected +++ b/java/ql/test/kotlin/library-tests/generic-instance-methods/test.expected @@ -14,8 +14,8 @@ calls | test.kt:22:15:22:33 | setter(...) | test.kt:12:1:25:1 | user | test.kt:0:0:0:0 | TestKt | file:///!unknown-binary-location/Generic.class:0:0:0:0 | setter | file:///!unknown-binary-location/Generic.class:0:0:0:0 | Generic | | test.kt:23:15:23:22 | getter(...) | test.kt:12:1:25:1 | user | test.kt:0:0:0:0 | TestKt | file:///!unknown-binary-location/Generic.class:0:0:0:0 | getter | file:///!unknown-binary-location/Generic.class:0:0:0:0 | Generic | constructors -| Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | Generic2 | Generic2(java.lang.String) | ? extends String | void | Test.java:1:7:1:14 | Generic2 | Test.java:3:10:3:17 | Generic2 | -| Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | Generic2 | Generic2(java.lang.Object) | ? super String | void | Test.java:1:7:1:14 | Generic2 | Test.java:3:10:3:17 | Generic2 | +| Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | Generic2 | Generic2() | | void | Test.java:1:7:1:14 | Generic2 | Test.java:3:10:3:17 | Generic2 | +| Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | Generic2 | Generic2(java.lang.String) | String | void | Test.java:1:7:1:14 | Generic2 | Test.java:3:10:3:17 | Generic2 | | Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | Generic2 | Generic2(java.lang.String) | String | void | Test.java:1:7:1:14 | Generic2 | Test.java:3:10:3:17 | Generic2 | | Test.java:1:7:1:14 | Generic2 | Test.java:3:10:3:17 | Generic2 | Generic2(java.lang.Object) | T | void | Test.java:1:7:1:14 | Generic2 | Test.java:3:10:3:17 | Generic2 | | Test.java:14:14:14:17 | Test | Test.java:14:14:14:17 | Test | Test() | No parameters | void | Test.java:14:14:14:17 | Test | Test.java:14:14:14:17 | Test | @@ -34,14 +34,14 @@ refTypes | test.kt:1:1:10:1 | Generic | | test.kt:1:15:1:15 | T | #select -| Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | getter | getter() | No parameters | ? extends String | Test.java:1:7:1:14 | Generic2 | Test.java:9:5:9:10 | getter | -| Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | identity | identity(java.lang.String) | ? extends String | ? extends String | Test.java:1:7:1:14 | Generic2 | Test.java:8:5:8:12 | identity | -| Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | identity2 | identity2(java.lang.String) | ? extends String | ? extends String | Test.java:1:7:1:14 | Generic2 | Test.java:7:5:7:13 | identity2 | -| Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | setter | setter(java.lang.String) | ? extends String | void | Test.java:1:7:1:14 | Generic2 | Test.java:10:8:10:13 | setter | -| Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | getter | getter() | No parameters | ? super String | Test.java:1:7:1:14 | Generic2 | Test.java:9:5:9:10 | getter | -| Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | identity | identity(java.lang.Object) | ? super String | ? super String | Test.java:1:7:1:14 | Generic2 | Test.java:8:5:8:12 | identity | -| Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | identity2 | identity2(java.lang.Object) | ? super String | ? super String | Test.java:1:7:1:14 | Generic2 | Test.java:7:5:7:13 | identity2 | -| Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | setter | setter(java.lang.Object) | ? super String | void | Test.java:1:7:1:14 | Generic2 | Test.java:10:8:10:13 | setter | +| Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | getter | getter() | No parameters | String | Test.java:1:7:1:14 | Generic2 | Test.java:9:5:9:10 | getter | +| Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | identity | identity() | | String | Test.java:1:7:1:14 | Generic2 | Test.java:8:5:8:12 | identity | +| Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | identity2 | identity2() | | String | Test.java:1:7:1:14 | Generic2 | Test.java:7:5:7:13 | identity2 | +| Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | setter | setter() | | void | Test.java:1:7:1:14 | Generic2 | Test.java:10:8:10:13 | setter | +| Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | getter | getter() | No parameters | Object | Test.java:1:7:1:14 | Generic2 | Test.java:9:5:9:10 | getter | +| Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | identity | identity(java.lang.String) | String | Object | Test.java:1:7:1:14 | Generic2 | Test.java:8:5:8:12 | identity | +| Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | identity2 | identity2(java.lang.String) | String | Object | Test.java:1:7:1:14 | Generic2 | Test.java:7:5:7:13 | identity2 | +| Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | setter | setter(java.lang.String) | String | void | Test.java:1:7:1:14 | Generic2 | Test.java:10:8:10:13 | setter | | Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | getter | getter() | No parameters | String | Test.java:1:7:1:14 | Generic2 | Test.java:9:5:9:10 | getter | | Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | identity | identity(java.lang.String) | String | String | Test.java:1:7:1:14 | Generic2 | Test.java:8:5:8:12 | identity | | Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | identity2 | identity2(java.lang.String) | String | String | Test.java:1:7:1:14 | Generic2 | Test.java:7:5:7:13 | identity2 | From 1737ed50ba8dd02c81167746393bd440b4d83c67 Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Wed, 20 Jul 2022 10:09:50 +0100 Subject: [PATCH 354/505] Add test cases for wildcard lowering of array types --- .../test/library-tests/wildcard-substitution/Lib.java | 3 +++ .../library-tests/wildcard-substitution/test.expected | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/java/ql/test/library-tests/wildcard-substitution/Lib.java b/java/ql/test/library-tests/wildcard-substitution/Lib.java index 9bd84a9218c..0647410b692 100644 --- a/java/ql/test/library-tests/wildcard-substitution/Lib.java +++ b/java/ql/test/library-tests/wildcard-substitution/Lib.java @@ -14,4 +14,7 @@ public class Lib { public List returnsExtends() { return null; } public List returnsSuper() { return null; } + public void takesArray(T[] ts) { } + public T[] returnsArray() { return null; } + } diff --git a/java/ql/test/library-tests/wildcard-substitution/test.expected b/java/ql/test/library-tests/wildcard-substitution/test.expected index 85af393c21e..91a1cc43cb0 100644 --- a/java/ql/test/library-tests/wildcard-substitution/test.expected +++ b/java/ql/test/library-tests/wildcard-substitution/test.expected @@ -1,38 +1,46 @@ +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsArray | CharSequence[] | | Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsExtends | List | | Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsInvar | List | | Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsSuper | List | | Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsUnbound | List | | Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsVar | CharSequence | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesArray | [] | | Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesExtends | List | | Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesInvar | List | | Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesSuper | List | | Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesUnbound | List | | Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesVar | | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsArray | Object[] | | Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsExtends | List | | Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsInvar | List | | Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsSuper | List | | Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsUnbound | List | | Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsVar | Object | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesArray | CharSequence[] | | Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesExtends | List | | Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesInvar | List | | Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesSuper | List | | Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesUnbound | List | | Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesVar | CharSequence | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsArray | Object[] | | Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsExtends | List | | Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsInvar | List | | Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsSuper | List | | Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsUnbound | List | | Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsVar | Object | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesArray | [] | | Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesExtends | List | | Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesInvar | List | | Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesSuper | List | | Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesUnbound | List | | Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesVar | | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsArray | CharSequence[] | | Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsExtends | List | | Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsInvar | List | | Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsSuper | List | | Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsUnbound | List | | Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsVar | CharSequence | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesArray | CharSequence[] | | Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesExtends | List | | Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesInvar | List | | Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesSuper | List | @@ -48,6 +56,8 @@ | Lib.java:3:14:3:16 | Lib | Lib.java:13:18:13:31 | returnsUnbound | List | | Lib.java:3:14:3:16 | Lib | Lib.java:14:28:14:41 | returnsExtends | List | | Lib.java:3:14:3:16 | Lib | Lib.java:15:26:15:37 | returnsSuper | List | +| Lib.java:3:14:3:16 | Lib | Lib.java:17:15:17:24 | takesArray | T[] | +| Lib.java:3:14:3:16 | Lib | Lib.java:18:14:18:25 | returnsArray | T[] | | User.java:1:14:1:17 | User | User.java:3:22:3:25 | test | Lib | | User.java:1:14:1:17 | User | User.java:3:22:3:25 | test | Lib | | User.java:1:14:1:17 | User | User.java:3:22:3:25 | test | Lib | From 985237ab2d3070edfc91c8d5c41596ec14362cdf Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Thu, 28 Jul 2022 17:05:52 +0200 Subject: [PATCH 355/505] Swift: small dispatcher fixes File extraction was not using named trap keys, and `emitDebugInfo` was using `std::forward` when it should not. --- swift/extractor/infra/SwiftDispatcher.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/swift/extractor/infra/SwiftDispatcher.h b/swift/extractor/infra/SwiftDispatcher.h index 60d20273655..d564673c976 100644 --- a/swift/extractor/infra/SwiftDispatcher.h +++ b/swift/extractor/infra/SwiftDispatcher.h @@ -215,7 +215,7 @@ class SwiftDispatcher { template void emitDebugInfo(const Args&... args) { - trap.debug(std::forward(args)...); + trap.debug(args...); } // In order to not emit duplicated entries for declarations, we restrict emission to only @@ -315,7 +315,7 @@ class SwiftDispatcher { virtual void visit(swift::TypeBase* type) = 0; void visit(const FilePath& file) { - auto entry = createEntry(file); + auto entry = createEntry(file, file.path); entry.name = file.path; emit(entry); } From e8747d3176688a54a9812fd02babdecdad99edd8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 28 Jul 2022 20:00:09 +0000 Subject: [PATCH 356/505] Post-release preparation for codeql-cli-2.10.2 --- cpp/ql/lib/qlpack.yml | 2 +- cpp/ql/src/qlpack.yml | 2 +- csharp/ql/campaigns/Solorigate/lib/qlpack.yml | 2 +- csharp/ql/campaigns/Solorigate/src/qlpack.yml | 2 +- csharp/ql/lib/qlpack.yml | 2 +- csharp/ql/src/qlpack.yml | 2 +- go/ql/lib/qlpack.yml | 2 +- go/ql/src/qlpack.yml | 2 +- java/ql/lib/qlpack.yml | 2 +- java/ql/src/qlpack.yml | 2 +- javascript/ql/lib/qlpack.yml | 2 +- javascript/ql/src/qlpack.yml | 2 +- python/ql/lib/qlpack.yml | 2 +- python/ql/src/qlpack.yml | 2 +- ruby/ql/lib/qlpack.yml | 2 +- ruby/ql/src/qlpack.yml | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) diff --git a/cpp/ql/lib/qlpack.yml b/cpp/ql/lib/qlpack.yml index 2761c28d94c..06e68dba48c 100644 --- a/cpp/ql/lib/qlpack.yml +++ b/cpp/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/cpp-all -version: 0.3.2 +version: 0.3.3-dev groups: cpp dbscheme: semmlecode.cpp.dbscheme extractor: cpp diff --git a/cpp/ql/src/qlpack.yml b/cpp/ql/src/qlpack.yml index b9902eb8bb4..03b90cb3668 100644 --- a/cpp/ql/src/qlpack.yml +++ b/cpp/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/cpp-queries -version: 0.3.1 +version: 0.3.2-dev groups: - cpp - queries diff --git a/csharp/ql/campaigns/Solorigate/lib/qlpack.yml b/csharp/ql/campaigns/Solorigate/lib/qlpack.yml index 08e6e1a8c82..78cc75ede63 100644 --- a/csharp/ql/campaigns/Solorigate/lib/qlpack.yml +++ b/csharp/ql/campaigns/Solorigate/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/csharp-solorigate-all -version: 1.2.2 +version: 1.2.3-dev groups: - csharp - solorigate diff --git a/csharp/ql/campaigns/Solorigate/src/qlpack.yml b/csharp/ql/campaigns/Solorigate/src/qlpack.yml index 89620dec618..fced50b6ef4 100644 --- a/csharp/ql/campaigns/Solorigate/src/qlpack.yml +++ b/csharp/ql/campaigns/Solorigate/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/csharp-solorigate-queries -version: 1.2.2 +version: 1.2.3-dev groups: - csharp - solorigate diff --git a/csharp/ql/lib/qlpack.yml b/csharp/ql/lib/qlpack.yml index d1409a61b13..8f932e28c7a 100644 --- a/csharp/ql/lib/qlpack.yml +++ b/csharp/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/csharp-all -version: 0.3.2 +version: 0.3.3-dev groups: csharp dbscheme: semmlecode.csharp.dbscheme extractor: csharp diff --git a/csharp/ql/src/qlpack.yml b/csharp/ql/src/qlpack.yml index c3e1381bf55..9f59ceafaf5 100644 --- a/csharp/ql/src/qlpack.yml +++ b/csharp/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/csharp-queries -version: 0.3.1 +version: 0.3.2-dev groups: - csharp - queries diff --git a/go/ql/lib/qlpack.yml b/go/ql/lib/qlpack.yml index 200393fbd6c..789f504c667 100644 --- a/go/ql/lib/qlpack.yml +++ b/go/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/go-all -version: 0.2.2 +version: 0.2.3-dev groups: go dbscheme: go.dbscheme extractor: go diff --git a/go/ql/src/qlpack.yml b/go/ql/src/qlpack.yml index df3aa78b2cf..b19c723b9c7 100644 --- a/go/ql/src/qlpack.yml +++ b/go/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/go-queries -version: 0.2.2 +version: 0.2.3-dev groups: - go - queries diff --git a/java/ql/lib/qlpack.yml b/java/ql/lib/qlpack.yml index 261f0508c36..5fe704a4f35 100644 --- a/java/ql/lib/qlpack.yml +++ b/java/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/java-all -version: 0.3.2 +version: 0.3.3-dev groups: java dbscheme: config/semmlecode.dbscheme extractor: java diff --git a/java/ql/src/qlpack.yml b/java/ql/src/qlpack.yml index 87c9e78e07f..8c0538014c1 100644 --- a/java/ql/src/qlpack.yml +++ b/java/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/java-queries -version: 0.3.1 +version: 0.3.2-dev groups: - java - queries diff --git a/javascript/ql/lib/qlpack.yml b/javascript/ql/lib/qlpack.yml index c1449f8acce..e559e82a56a 100644 --- a/javascript/ql/lib/qlpack.yml +++ b/javascript/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/javascript-all -version: 0.2.2 +version: 0.2.3-dev groups: javascript dbscheme: semmlecode.javascript.dbscheme extractor: javascript diff --git a/javascript/ql/src/qlpack.yml b/javascript/ql/src/qlpack.yml index 72dda406008..9852441a368 100644 --- a/javascript/ql/src/qlpack.yml +++ b/javascript/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/javascript-queries -version: 0.3.1 +version: 0.3.2-dev groups: - javascript - queries diff --git a/python/ql/lib/qlpack.yml b/python/ql/lib/qlpack.yml index 5cd0847d929..20d79f44e49 100644 --- a/python/ql/lib/qlpack.yml +++ b/python/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/python-all -version: 0.5.2 +version: 0.5.3-dev groups: python dbscheme: semmlecode.python.dbscheme extractor: python diff --git a/python/ql/src/qlpack.yml b/python/ql/src/qlpack.yml index c70cb344e92..75227225c64 100644 --- a/python/ql/src/qlpack.yml +++ b/python/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/python-queries -version: 0.4.0 +version: 0.4.1-dev groups: - python - queries diff --git a/ruby/ql/lib/qlpack.yml b/ruby/ql/lib/qlpack.yml index 6cf140325c0..5a763d9c3dd 100644 --- a/ruby/ql/lib/qlpack.yml +++ b/ruby/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/ruby-all -version: 0.3.2 +version: 0.3.3-dev groups: ruby extractor: ruby dbscheme: ruby.dbscheme diff --git a/ruby/ql/src/qlpack.yml b/ruby/ql/src/qlpack.yml index 17eb743f26d..b713a6c49e3 100644 --- a/ruby/ql/src/qlpack.yml +++ b/ruby/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/ruby-queries -version: 0.3.1 +version: 0.3.2-dev groups: - ruby - queries From c29eb814b27e47e50aa8926ae4926b52d9517f6a Mon Sep 17 00:00:00 2001 From: Harry Maclean Date: Fri, 29 Jul 2022 10:44:36 +1200 Subject: [PATCH 357/505] Ruby: Reorganise ActionDispatch framework Put routing modelling inside a Routing module. --- .../ruby/frameworks/ActionController.qll | 6 +- .../codeql/ruby/frameworks/ActionDispatch.qll | 1817 +++++++++-------- .../frameworks/ActionDispatch.ql | 6 +- 3 files changed, 920 insertions(+), 909 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll index 90b00ecde3f..b32d368dad3 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll @@ -83,7 +83,7 @@ class ActionControllerActionMethod extends Method, HTTP::Server::RequestHandler: * Gets a route to this handler, if one exists. * May return multiple results. */ - ActionDispatch::Route getARoute() { + ActionDispatch::Routing::Route getARoute() { exists(string name | isRoute(result, name, controllerClass) and isActionControllerMethod(this, name, controllerClass) @@ -93,10 +93,10 @@ class ActionControllerActionMethod extends Method, HTTP::Server::RequestHandler: pragma[nomagic] private predicate isRoute( - ActionDispatch::Route route, string name, ActionControllerControllerClass controllerClass + ActionDispatch::Routing::Route route, string name, ActionControllerControllerClass controllerClass ) { route.getController() + "_controller" = - ActionDispatch::underscore(namespaceDeclaration(controllerClass)) and + ActionDispatch::Routing::underscore(namespaceDeclaration(controllerClass)) and name = route.getAction() } diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActionDispatch.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActionDispatch.qll index bd16ef3796e..35a198ef7f2 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/ActionDispatch.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActionDispatch.qll @@ -1,5 +1,6 @@ /** - * Models routing configuration specified using the `ActionDispatch` library, which is part of Rails. + * Models the `ActionDispatch` HTTP library, which is part of Rails. + * Version: 7.1.0. */ private import codeql.ruby.AST @@ -7,953 +8,963 @@ private import codeql.ruby.Concepts private import codeql.ruby.DataFlow /** - * Models routing configuration specified using the `ActionDispatch` library, which is part of Rails. + * Models the `ActionDispatch` HTTP library, which is part of Rails. + * Version: 7.1.0. */ module ActionDispatch { /** - * A block that defines some routes. - * Route blocks can contribute to the path or controller namespace of their child routes. - * For example, in the block below - * ```rb - * scope path: "/admin" do - * get "/dashboard", to: "admin_dashboard#show" - * end - * ``` - * the route defined by the call to `get` has the full path `/admin/dashboard`. - * We track these contributions via `getPathComponent` and `getControllerComponent`. + * Models routing configuration specified using the `ActionDispatch` library, which is part of Rails. */ - abstract private class RouteBlock extends TRouteBlock { + module Routing { /** - * Gets the name of a primary CodeQL class to which this route block belongs. - */ - string getAPrimaryQlClass() { result = "RouteBlock" } - - /** - * Gets a string representation of this route block. - */ - string toString() { none() } - - /** - * Gets a `Stmt` within this route block. - */ - abstract Stmt getAStmt(); - - /** - * Gets the parent of this route block, if one exists. - */ - abstract RouteBlock getParent(); - - /** - * Gets the `n`th parent of this route block. - * The zeroth parent is this block, the first parent is the direct parent of this block, etc. - */ - RouteBlock getParent(int n) { - if n = 0 then result = this else result = this.getParent().getParent(n - 1) - } - - /** - * Gets the component of the path defined by this block, if it exists. - */ - abstract string getPathComponent(); - - /** - * Gets the component of the controller namespace defined by this block, if it exists. - */ - abstract string getControllerComponent(); - - /** - * Gets the location of this route block. - */ - abstract Location getLocation(); - } - - /** - * A route block that is not the top-level block. - * This block will always have a parent. - */ - abstract private class NestedRouteBlock extends RouteBlock { - RouteBlock parent; - - override RouteBlock getParent() { result = parent } - - override string getAPrimaryQlClass() { result = "NestedRouteBlock" } - } - - /** - * A top-level routes block. - * ```rb - * Rails.application.routes.draw do - * ... - * end - * ``` - */ - private class TopLevelRouteBlock extends RouteBlock, TTopLevelRouteBlock { - MethodCall call; - // Routing blocks create scopes which define the namespace for controllers and paths, - // though they can be overridden in various ways. - // The namespaces can differ, so we track them separately. - Block block; - - TopLevelRouteBlock() { this = TTopLevelRouteBlock(_, call, block) } - - override string getAPrimaryQlClass() { result = "TopLevelRouteBlock" } - - Block getBlock() { result = block } - - override Stmt getAStmt() { result = block.getAStmt() } - - override RouteBlock getParent() { none() } - - override string toString() { result = call.toString() } - - override Location getLocation() { result = call.getLocation() } - - override string getPathComponent() { none() } - - override string getControllerComponent() { none() } - } - - /** - * A route block defined by a call to `constraints`. - * ```rb - * constraints(foo: /some_regex/) do - * get "/posts/:foo", to "posts#something" - * end - * ``` - * https://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Scoping.html#method-i-constraints - */ - private class ConstraintsRouteBlock extends NestedRouteBlock, TConstraintsRouteBlock { - private Block block; - private MethodCall call; - - ConstraintsRouteBlock() { this = TConstraintsRouteBlock(parent, call, block) } - - override string getAPrimaryQlClass() { result = "ConstraintsRouteBlock" } - - override Stmt getAStmt() { result = block.getAStmt() } - - override string getPathComponent() { result = "" } - - override string getControllerComponent() { result = "" } - - override string toString() { result = call.toString() } - - override Location getLocation() { result = call.getLocation() } - } - - /** - * A route block defined by a call to `scope`. - * ```rb - * scope(path: "/some_path", module: "some_module") do - * get "/posts/:foo", to "posts#something" - * end - * ``` - * https://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Scoping.html#method-i-scope - */ - private class ScopeRouteBlock extends NestedRouteBlock, TScopeRouteBlock { - private MethodCall call; - private Block block; - - ScopeRouteBlock() { this = TScopeRouteBlock(parent, call, block) } - - override string getAPrimaryQlClass() { result = "ScopeRouteBlock" } - - override Stmt getAStmt() { result = block.getAStmt() } - - override string toString() { result = call.toString() } - - override Location getLocation() { result = call.getLocation() } - - override string getPathComponent() { - call.getKeywordArgument("path").getConstantValue().isStringlikeValue(result) - or - not exists(call.getKeywordArgument("path")) and - call.getArgument(0).getConstantValue().isStringlikeValue(result) - } - - override string getControllerComponent() { - call.getKeywordArgument(["controller", "module"]).getConstantValue().isStringlikeValue(result) - } - } - - /** - * A route block defined by a call to `resources`. - * ```rb - * resources :articles do - * get "/comments", to "comments#index" - * end - * ``` - * https://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Resources.html#method-i-resources - */ - private class ResourcesRouteBlock extends NestedRouteBlock, TResourcesRouteBlock { - private MethodCall call; - private Block block; - - ResourcesRouteBlock() { this = TResourcesRouteBlock(parent, call, block) } - - override string getAPrimaryQlClass() { result = "ResourcesRouteBlock" } - - override Stmt getAStmt() { result = block.getAStmt() } - - /** - * Gets the `resources` call that gives rise to this route block. - */ - MethodCall getDefiningMethodCall() { result = call } - - override string getPathComponent() { - exists(string resource | call.getArgument(0).getConstantValue().isStringlikeValue(resource) | - result = resource + "/:" + singularize(resource) + "_id" - ) - } - - override string getControllerComponent() { result = "" } - - override string toString() { result = call.toString() } - - override Location getLocation() { result = call.getLocation() } - } - - /** - * A route block that is guarded by a conditional statement. - * For example: - * ```rb - * if Rails.env.test? - * get "/foo/bar", to: "foo#bar" - * end - * ``` - * We ignore the condition and analyze both branches to obtain as - * much routing information as possible. - */ - private class ConditionalRouteBlock extends NestedRouteBlock, TConditionalRouteBlock { - private ConditionalExpr e; - - ConditionalRouteBlock() { this = TConditionalRouteBlock(parent, e) } - - override string getAPrimaryQlClass() { result = "ConditionalRouteBlock" } - - override Stmt getAStmt() { result = e.getBranch(_).(StmtSequence).getAStmt() } - - override string getPathComponent() { none() } - - override string getControllerComponent() { none() } - - override string toString() { result = e.toString() } - - override Location getLocation() { result = e.getLocation() } - } - - /** - * A route block defined by a call to `namespace`. - * ```rb - * namespace :admin do - * resources :posts - * end - * ``` - * https://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Scoping.html#method-i-namespace - */ - private class NamespaceRouteBlock extends NestedRouteBlock, TNamespaceRouteBlock { - private MethodCall call; - private Block block; - - NamespaceRouteBlock() { this = TNamespaceRouteBlock(parent, call, block) } - - override Stmt getAStmt() { result = block.getAStmt() } - - override string getPathComponent() { result = this.getNamespace() } - - override string getControllerComponent() { result = this.getNamespace() } - - private string getNamespace() { - call.getArgument(0).getConstantValue().isStringlikeValue(result) - } - - override string toString() { result = call.toString() } - - override Location getLocation() { result = call.getLocation() } - } - - /** - * A route configuration. This defines a combination of HTTP method and URL - * path which should be routed to a particular controller-action pair. - * This can arise from an explicit call to a routing method, for example: - * ```rb - * get "/photos", to: "photos#index" - * ``` - * or via a convenience method like `resources`, which defines multiple routes at once: - * ```rb - * resources :photos - * ``` - */ - class Route extends TRoute instanceof RouteImpl { - /** - * Gets the name of a primary CodeQL class to which this route belongs. - */ - string getAPrimaryQlClass() { result = "Route" } - - /** Gets a string representation of this route. */ - string toString() { result = super.toString() } - - /** - * Gets the location of the method call that defines this route. - */ - Location getLocation() { result = super.getLocation() } - - /** - * Gets the full controller targeted by this route. - */ - string getController() { result = super.getController() } - - /** - * Gets the action targeted by this route. - */ - string getAction() { result = super.getAction() } - - /** - * Gets the HTTP method of this route. - * The result is one of [get, post, put, patch, delete]. - */ - string getHttpMethod() { result = super.getHttpMethod() } - - /** - * Gets the full path of the route. - */ - string getPath() { result = super.getPath() } - - /** - * Get a URL capture. This is a wildcard URL segment whose value is placed in `params`. - * For example, in - * ```ruby - * get "/foo/:bar/baz", to: "users#index" + * A block that defines some routes. + * Route blocks can contribute to the path or controller namespace of their child routes. + * For example, in the block below + * ```rb + * scope path: "/admin" do + * get "/dashboard", to: "admin_dashboard#show" + * end * ``` - * the capture is `:bar`. + * the route defined by the call to `get` has the full path `/admin/dashboard`. + * We track these contributions via `getPathComponent` and `getControllerComponent`. */ - string getACapture() { result = super.getACapture() } - } + abstract private class RouteBlock extends TRouteBlock { + /** + * Gets the name of a primary CodeQL class to which this route block belongs. + */ + string getAPrimaryQlClass() { result = "RouteBlock" } - /** - * The implementation of `Route`. - * This class is abstract and is thus kept private so we don't expose it to - * users. - * Extend this class to add new instances of routes. - */ - abstract private class RouteImpl extends TRoute { - /** - * Gets the name of a primary CodeQL class to which this route belongs. - */ - string getAPrimaryQlClass() { result = "RouteImpl" } + /** + * Gets a string representation of this route block. + */ + string toString() { none() } - MethodCall method; + /** + * Gets a `Stmt` within this route block. + */ + abstract Stmt getAStmt(); - /** Gets a string representation of this route. */ - string toString() { result = method.toString() } + /** + * Gets the parent of this route block, if one exists. + */ + abstract RouteBlock getParent(); + + /** + * Gets the `n`th parent of this route block. + * The zeroth parent is this block, the first parent is the direct parent of this block, etc. + */ + RouteBlock getParent(int n) { + if n = 0 then result = this else result = this.getParent().getParent(n - 1) + } + + /** + * Gets the component of the path defined by this block, if it exists. + */ + abstract string getPathComponent(); + + /** + * Gets the component of the controller namespace defined by this block, if it exists. + */ + abstract string getControllerComponent(); + + /** + * Gets the location of this route block. + */ + abstract Location getLocation(); + } /** - * Gets the location of the method call that defines this route. + * A route block that is not the top-level block. + * This block will always have a parent. */ - Location getLocation() { result = method.getLocation() } + abstract private class NestedRouteBlock extends RouteBlock { + RouteBlock parent; + + override RouteBlock getParent() { result = parent } + + override string getAPrimaryQlClass() { result = "NestedRouteBlock" } + } /** - * Gets the method call that defines this route. + * A top-level routes block. + * ```rb + * Rails.application.routes.draw do + * ... + * end + * ``` */ - MethodCall getDefiningMethodCall() { result = method } + private class TopLevelRouteBlock extends RouteBlock, TTopLevelRouteBlock { + MethodCall call; + // Routing blocks create scopes which define the namespace for controllers and paths, + // though they can be overridden in various ways. + // The namespaces can differ, so we track them separately. + Block block; + + TopLevelRouteBlock() { this = TTopLevelRouteBlock(_, call, block) } + + override string getAPrimaryQlClass() { result = "TopLevelRouteBlock" } + + Block getBlock() { result = block } + + override Stmt getAStmt() { result = block.getAStmt() } + + override RouteBlock getParent() { none() } + + override string toString() { result = call.toString() } + + override Location getLocation() { result = call.getLocation() } + + override string getPathComponent() { none() } + + override string getControllerComponent() { none() } + } /** - * Get the last component of the path. For example, in + * A route block defined by a call to `constraints`. + * ```rb + * constraints(foo: /some_regex/) do + * get "/posts/:foo", to "posts#something" + * end + * ``` + * https://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Scoping.html#method-i-constraints + */ + private class ConstraintsRouteBlock extends NestedRouteBlock, TConstraintsRouteBlock { + private Block block; + private MethodCall call; + + ConstraintsRouteBlock() { this = TConstraintsRouteBlock(parent, call, block) } + + override string getAPrimaryQlClass() { result = "ConstraintsRouteBlock" } + + override Stmt getAStmt() { result = block.getAStmt() } + + override string getPathComponent() { result = "" } + + override string getControllerComponent() { result = "" } + + override string toString() { result = call.toString() } + + override Location getLocation() { result = call.getLocation() } + } + + /** + * A route block defined by a call to `scope`. + * ```rb + * scope(path: "/some_path", module: "some_module") do + * get "/posts/:foo", to "posts#something" + * end + * ``` + * https://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Scoping.html#method-i-scope + */ + private class ScopeRouteBlock extends NestedRouteBlock, TScopeRouteBlock { + private MethodCall call; + private Block block; + + ScopeRouteBlock() { this = TScopeRouteBlock(parent, call, block) } + + override string getAPrimaryQlClass() { result = "ScopeRouteBlock" } + + override Stmt getAStmt() { result = block.getAStmt() } + + override string toString() { result = call.toString() } + + override Location getLocation() { result = call.getLocation() } + + override string getPathComponent() { + call.getKeywordArgument("path").getConstantValue().isStringlikeValue(result) + or + not exists(call.getKeywordArgument("path")) and + call.getArgument(0).getConstantValue().isStringlikeValue(result) + } + + override string getControllerComponent() { + call.getKeywordArgument(["controller", "module"]) + .getConstantValue() + .isStringlikeValue(result) + } + } + + /** + * A route block defined by a call to `resources`. + * ```rb + * resources :articles do + * get "/comments", to "comments#index" + * end + * ``` + * https://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Resources.html#method-i-resources + */ + private class ResourcesRouteBlock extends NestedRouteBlock, TResourcesRouteBlock { + private MethodCall call; + private Block block; + + ResourcesRouteBlock() { this = TResourcesRouteBlock(parent, call, block) } + + override string getAPrimaryQlClass() { result = "ResourcesRouteBlock" } + + override Stmt getAStmt() { result = block.getAStmt() } + + /** + * Gets the `resources` call that gives rise to this route block. + */ + MethodCall getDefiningMethodCall() { result = call } + + override string getPathComponent() { + exists(string resource | + call.getArgument(0).getConstantValue().isStringlikeValue(resource) + | + result = resource + "/:" + singularize(resource) + "_id" + ) + } + + override string getControllerComponent() { result = "" } + + override string toString() { result = call.toString() } + + override Location getLocation() { result = call.getLocation() } + } + + /** + * A route block that is guarded by a conditional statement. + * For example: + * ```rb + * if Rails.env.test? + * get "/foo/bar", to: "foo#bar" + * end + * ``` + * We ignore the condition and analyze both branches to obtain as + * much routing information as possible. + */ + private class ConditionalRouteBlock extends NestedRouteBlock, TConditionalRouteBlock { + private ConditionalExpr e; + + ConditionalRouteBlock() { this = TConditionalRouteBlock(parent, e) } + + override string getAPrimaryQlClass() { result = "ConditionalRouteBlock" } + + override Stmt getAStmt() { result = e.getBranch(_).(StmtSequence).getAStmt() } + + override string getPathComponent() { none() } + + override string getControllerComponent() { none() } + + override string toString() { result = e.toString() } + + override Location getLocation() { result = e.getLocation() } + } + + /** + * A route block defined by a call to `namespace`. + * ```rb + * namespace :admin do + * resources :posts + * end + * ``` + * https://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Scoping.html#method-i-namespace + */ + private class NamespaceRouteBlock extends NestedRouteBlock, TNamespaceRouteBlock { + private MethodCall call; + private Block block; + + NamespaceRouteBlock() { this = TNamespaceRouteBlock(parent, call, block) } + + override Stmt getAStmt() { result = block.getAStmt() } + + override string getPathComponent() { result = this.getNamespace() } + + override string getControllerComponent() { result = this.getNamespace() } + + private string getNamespace() { + call.getArgument(0).getConstantValue().isStringlikeValue(result) + } + + override string toString() { result = call.toString() } + + override Location getLocation() { result = call.getLocation() } + } + + /** + * A route configuration. This defines a combination of HTTP method and URL + * path which should be routed to a particular controller-action pair. + * This can arise from an explicit call to a routing method, for example: * ```rb * get "/photos", to: "photos#index" * ``` - * this is `/photos`. - * If the string has any interpolations, this predicate will have no result. - */ - abstract string getLastPathComponent(); - - /** - * Gets the HTTP method of this route. - * The result is one of [get, post, put, patch, delete]. - */ - abstract string getHttpMethod(); - - /** - * Gets the last controller component. - * This is the controller specified in the route itself. - */ - abstract string getLastControllerComponent(); - - /** - * Gets a component of the controller. - * This behaves identically to `getPathComponent`, but for controller information. - */ - string getControllerComponent(int n) { - if n = 0 - then result = this.getLastControllerComponent() - else result = this.getParentBlock().getParent(n - 1).getControllerComponent() - } - - /** - * Gets the full controller targeted by this route. - */ - string getController() { - result = - concat(int n | - this.getControllerComponent(n) != "" - | - this.getControllerComponent(n), "/" order by n desc - ) - } - - /** - * Gets the action targeted by this route. - */ - abstract string getAction(); - - /** - * Gets the parent `RouteBlock` of this route. - */ - abstract RouteBlock getParentBlock(); - - /** - * Gets a component of the path. Components are numbered from 0 up, where 0 - * is the last component, 1 is the second-last, etc. - * For example, in the following route: - * + * or via a convenience method like `resources`, which defines multiple routes at once: * ```rb - * namespace path: "foo" do - * namespace path: "bar" do - * get "baz", to: "foo#bar - * end - * end + * resources :photos * ``` - * - * the components are: - * - * | n | component - * |---|---------- - * | 0 | baz - * | 1 | bar - * | 2 | foo */ - string getPathComponent(int n) { - if n = 0 - then result = this.getLastPathComponent() - else result = this.getParentBlock().getParent(n - 1).getPathComponent() + class Route extends TRoute instanceof RouteImpl { + /** + * Gets the name of a primary CodeQL class to which this route belongs. + */ + string getAPrimaryQlClass() { result = "Route" } + + /** Gets a string representation of this route. */ + string toString() { result = super.toString() } + + /** + * Gets the location of the method call that defines this route. + */ + Location getLocation() { result = super.getLocation() } + + /** + * Gets the full controller targeted by this route. + */ + string getController() { result = super.getController() } + + /** + * Gets the action targeted by this route. + */ + string getAction() { result = super.getAction() } + + /** + * Gets the HTTP method of this route. + * The result is one of [get, post, put, patch, delete]. + */ + string getHttpMethod() { result = super.getHttpMethod() } + + /** + * Gets the full path of the route. + */ + string getPath() { result = super.getPath() } + + /** + * Get a URL capture. This is a wildcard URL segment whose value is placed in `params`. + * For example, in + * ```ruby + * get "/foo/:bar/baz", to: "users#index" + * ``` + * the capture is `:bar`. + */ + string getACapture() { result = super.getACapture() } } /** - * Gets the full path of the route. + * The implementation of `Route`. + * This class is abstract and is thus kept private so we don't expose it to + * users. + * Extend this class to add new instances of routes. */ - string getPath() { - result = - concat(int n | - this.getPathComponent(n) != "" - | - // Strip leading and trailing slashes from each path component before combining - stripSlashes(this.getPathComponent(n)), "/" order by n desc - ) - } + abstract private class RouteImpl extends TRoute { + /** + * Gets the name of a primary CodeQL class to which this route belongs. + */ + string getAPrimaryQlClass() { result = "RouteImpl" } - /** - * Get a URL capture. This is a wildcard URL segment whose value is placed in `params`. - * For example, in - * ```ruby - * get "/foo/:bar/baz", to: "users#index" - * ``` - * the capture is `:bar`. - * We don't currently make use of this, but it may be useful in future to more accurately - * model the contents of the `params` hash. - */ - string getACapture() { result = this.getPathComponent(_).regexpFind(":[^:/]+", _, _) } - } + MethodCall method; - /** - * A route generated by an explicit call to `get`, `post`, etc. - * - * ```ruby - * get "/photos", to: "photos#index" - * put "/photos/:id", to: "photos#update" - * ``` - */ - private class ExplicitRoute extends RouteImpl, TExplicitRoute { - RouteBlock parentBlock; + /** Gets a string representation of this route. */ + string toString() { result = method.toString() } - ExplicitRoute() { this = TExplicitRoute(parentBlock, method) } + /** + * Gets the location of the method call that defines this route. + */ + Location getLocation() { result = method.getLocation() } - override string getAPrimaryQlClass() { result = "ExplicitRoute" } + /** + * Gets the method call that defines this route. + */ + MethodCall getDefiningMethodCall() { result = method } - override RouteBlock getParentBlock() { result = parentBlock } + /** + * Get the last component of the path. For example, in + * ```rb + * get "/photos", to: "photos#index" + * ``` + * this is `/photos`. + * If the string has any interpolations, this predicate will have no result. + */ + abstract string getLastPathComponent(); - override string getLastPathComponent() { - method.getArgument(0).getConstantValue().isStringlikeValue(result) - } + /** + * Gets the HTTP method of this route. + * The result is one of [get, post, put, patch, delete]. + */ + abstract string getHttpMethod(); - override string getLastControllerComponent() { - method.getKeywordArgument("controller").getConstantValue().isStringlikeValue(result) - or - not exists(method.getKeywordArgument("controller")) and - ( - result = extractController(this.getActionString()) - or - // If controller is not specified, and we're in a `resources` route block, use the controller of that route. - // For example, in - // - // resources :posts do - // get "timestamp", to: :timestamp - // end - // - // The route is GET /posts/:post_id/timestamp => posts/timestamp - not exists(extractController(this.getActionString())) and - exists(ResourcesRoute r | - r.getDefiningMethodCall() = parentBlock.(ResourcesRouteBlock).getDefiningMethodCall() - | - result = r.getLastControllerComponent() - ) - ) - } + /** + * Gets the last controller component. + * This is the controller specified in the route itself. + */ + abstract string getLastControllerComponent(); - private string getActionString() { - method.getKeywordArgument("to").getConstantValue().isStringlikeValue(result) - or - method.getKeywordArgument("to").(MethodCall).getMethodName() = "redirect" and - result = "#" - } - - override string getAction() { - // get "/photos", action: "index" - method.getKeywordArgument("action").getConstantValue().isStringlikeValue(result) - or - not exists(method.getKeywordArgument("action")) and - ( - // get "/photos", to: "photos#index" - // get "/photos", to: redirect("some_url") - result = extractAction(this.getActionString()) - or - // resources :photos, only: [] do - // get "/", to: "index" - // end - not exists(extractAction(this.getActionString())) and result = this.getActionString() - or - // get :some_action - not exists(this.getActionString()) and - method.getArgument(0).getConstantValue().isStringlikeValue(result) - ) - } - - override string getHttpMethod() { result = method.getMethodName().toString() } - } - - /** - * A route generated by a call to `resources`. - * - * ```ruby - * resources :photos - * ``` - * This creates eight routes, equivalent to the following code: - * ```ruby - * get "/photos", to: "photos#index" - * get "/photos/new", to: "photos#new" - * post "/photos", to: "photos#create" - * get "/photos/:id", to: "photos#show" - * get "/photos/:id/edit", to: "photos#edit" - * patch "/photos/:id", to: "photos#update" - * put "/photos/:id", to: "photos#update" - * delete "/photos/:id", to: "photos#delete" - * ``` - * - * `resources` can take a block. Any routes defined inside the block will inherit a path component of - * `//:_id`. For example: - * - * ```ruby - * resources :photos do - * get "/foo", to: "photos#foo" - * end - * ``` - * This creates the eight default routes, plus one more, which is nested under "/photos/:photo_id", equivalent to: - * ```ruby - * get "/photos/:photo_id/foo", to: "photos#foo" - * ``` - */ - private class ResourcesRoute extends RouteImpl, TResourcesRoute { - RouteBlock parent; - string action; - string httpMethod; - string pathComponent; - - ResourcesRoute() { - exists(string resource | - this = TResourcesRoute(parent, method, action) and - method.getArgument(0).getConstantValue().isStringlikeValue(resource) and - isDefaultResourceRoute(resource, httpMethod, pathComponent, action) - ) - } - - override string getAPrimaryQlClass() { result = "ResourcesRoute" } - - override RouteBlock getParentBlock() { result = parent } - - override string getLastPathComponent() { result = pathComponent } - - override string getLastControllerComponent() { - method.getArgument(0).getConstantValue().isStringlikeValue(result) - } - - override string getAction() { result = action } - - override string getHttpMethod() { result = httpMethod } - } - - /** - * A route generated by a call to `resource`. - * This is like a `resources` route, but creates routes for a singular resource. - * This means there's no index route, no id parameter, and the resource name is expected to be singular. - * It will still be routed to a pluralised controller name. - * ```ruby - * resource :account - * ``` - */ - private class SingularResourceRoute extends RouteImpl, TResourceRoute { - RouteBlock parent; - string action; - string httpMethod; - string pathComponent; - - SingularResourceRoute() { - exists(string resource | - this = TResourceRoute(parent, method, action) and - method.getArgument(0).getConstantValue().isStringlikeValue(resource) and - isDefaultSingularResourceRoute(resource, httpMethod, pathComponent, action) - ) - } - - override string getAPrimaryQlClass() { result = "SingularResourceRoute" } - - override RouteBlock getParentBlock() { result = parent } - - override string getLastPathComponent() { result = pathComponent } - - override string getLastControllerComponent() { - method.getArgument(0).getConstantValue().isStringlikeValue(result) - } - - override string getAction() { result = action } - - override string getHttpMethod() { result = httpMethod } - } - - /** - * A route generated by a call to `match`. - * This is a lower level primitive that powers `get`, `post` etc. - * The first argument can be a path or a (path, controller-action) pair. - * The controller, action and HTTP method can be specified with the - * `controller:`, `action:` and `via:` keyword arguments, respectively. - * ```ruby - * match 'photos/:id' => 'photos#show', via: :get - * match 'photos/:id', to: 'photos#show', via: :get - * match 'photos/:id', to 'photos#show', via: [:get, :post] - * match 'photos/:id', controller: 'photos', action: 'show', via: :get - * ``` - */ - private class MatchRoute extends RouteImpl, TMatchRoute { - private RouteBlock parent; - - MatchRoute() { this = TMatchRoute(parent, method) } - - override string getAPrimaryQlClass() { result = "MatchRoute" } - - override RouteBlock getParentBlock() { result = parent } - - override string getLastPathComponent() { - [method.getArgument(0), method.getArgument(0).(Pair).getKey()] - .getConstantValue() - .isStringlikeValue(result) - } - - override string getLastControllerComponent() { - result = - extractController(method.getKeywordArgument("to").getConstantValue().getStringlikeValue()) or - method.getKeywordArgument("controller").getConstantValue().isStringlikeValue(result) or - result = - extractController(method - .getArgument(0) - .(Pair) - .getValue() - .getConstantValue() - .getStringlikeValue()) - } - - override string getHttpMethod() { - exists(string via | - method.getKeywordArgument("via").getConstantValue().isStringlikeValue(via) - | - via = "all" and result = anyHttpMethod() - or - via != "all" and result = via - ) - or - result = - method - .getKeywordArgument("via") - .(ArrayLiteral) - .getElement(_) - .getConstantValue() - .getStringlikeValue() - } - - override string getAction() { - result = - extractAction(method.getKeywordArgument("to").getConstantValue().getStringlikeValue()) or - method.getKeywordArgument("action").getConstantValue().isStringlikeValue(result) or - result = - extractAction(method - .getArgument(0) - .(Pair) - .getValue() - .getConstantValue() - .getStringlikeValue()) - } - } - - private import Cached - - /** - * This module contains the IPA types backing `RouteBlock` and `Route`, cached for performance. - */ - cached - private module Cached { - cached - newtype TRouteBlock = - TTopLevelRouteBlock(MethodCall routes, MethodCall draw, Block b) { - routes.getMethodName() = "routes" and - draw.getMethodName() = "draw" and - draw.getReceiver() = routes and - draw.getBlock() = b - } or - // constraints(foo: /some_regex/) do - // get "/posts/:foo", to "posts#something" - // end - TConstraintsRouteBlock(RouteBlock parent, MethodCall constraints, Block b) { - parent.getAStmt() = constraints and - constraints.getMethodName() = "constraints" and - constraints.getBlock() = b - } or - // scope(path: "/some_path", module: "some_module") do - // get "/posts/:foo", to "posts#something" - // end - TScopeRouteBlock(RouteBlock parent, MethodCall scope, Block b) { - parent.getAStmt() = scope and scope.getMethodName() = "scope" and scope.getBlock() = b - } or - // resources :articles do - // get "/comments", to "comments#index" - // end - TResourcesRouteBlock(RouteBlock parent, MethodCall resources, Block b) { - parent.getAStmt() = resources and - resources.getMethodName() = "resources" and - resources.getBlock() = b - } or - // A conditional statement guarding some routes. - // We ignore the condition and analyze both branches to obtain as - // much routing information as possible. - TConditionalRouteBlock(RouteBlock parent, ConditionalExpr e) { parent.getAStmt() = e } or - // namespace :admin do - // resources :posts - // end - TNamespaceRouteBlock(RouteBlock parent, MethodCall namespace, Block b) { - parent.getAStmt() = namespace and - namespace.getMethodName() = "namespace" and - namespace.getBlock() = b + /** + * Gets a component of the controller. + * This behaves identically to `getPathComponent`, but for controller information. + */ + string getControllerComponent(int n) { + if n = 0 + then result = this.getLastControllerComponent() + else result = this.getParentBlock().getParent(n - 1).getControllerComponent() } + /** + * Gets the full controller targeted by this route. + */ + string getController() { + result = + concat(int n | + this.getControllerComponent(n) != "" + | + this.getControllerComponent(n), "/" order by n desc + ) + } + + /** + * Gets the action targeted by this route. + */ + abstract string getAction(); + + /** + * Gets the parent `RouteBlock` of this route. + */ + abstract RouteBlock getParentBlock(); + + /** + * Gets a component of the path. Components are numbered from 0 up, where 0 + * is the last component, 1 is the second-last, etc. + * For example, in the following route: + * + * ```rb + * namespace path: "foo" do + * namespace path: "bar" do + * get "baz", to: "foo#bar + * end + * end + * ``` + * + * the components are: + * + * | n | component + * |---|---------- + * | 0 | baz + * | 1 | bar + * | 2 | foo + */ + string getPathComponent(int n) { + if n = 0 + then result = this.getLastPathComponent() + else result = this.getParentBlock().getParent(n - 1).getPathComponent() + } + + /** + * Gets the full path of the route. + */ + string getPath() { + result = + concat(int n | + this.getPathComponent(n) != "" + | + // Strip leading and trailing slashes from each path component before combining + stripSlashes(this.getPathComponent(n)), "/" order by n desc + ) + } + + /** + * Get a URL capture. This is a wildcard URL segment whose value is placed in `params`. + * For example, in + * ```ruby + * get "/foo/:bar/baz", to: "users#index" + * ``` + * the capture is `:bar`. + * We don't currently make use of this, but it may be useful in future to more accurately + * model the contents of the `params` hash. + */ + string getACapture() { result = this.getPathComponent(_).regexpFind(":[^:/]+", _, _) } + } + /** - * A route configuration. See `Route` for more info + * A route generated by an explicit call to `get`, `post`, etc. + * + * ```ruby + * get "/photos", to: "photos#index" + * put "/photos/:id", to: "photos#update" + * ``` + */ + private class ExplicitRoute extends RouteImpl, TExplicitRoute { + RouteBlock parentBlock; + + ExplicitRoute() { this = TExplicitRoute(parentBlock, method) } + + override string getAPrimaryQlClass() { result = "ExplicitRoute" } + + override RouteBlock getParentBlock() { result = parentBlock } + + override string getLastPathComponent() { + method.getArgument(0).getConstantValue().isStringlikeValue(result) + } + + override string getLastControllerComponent() { + method.getKeywordArgument("controller").getConstantValue().isStringlikeValue(result) + or + not exists(method.getKeywordArgument("controller")) and + ( + result = extractController(this.getActionString()) + or + // If controller is not specified, and we're in a `resources` route block, use the controller of that route. + // For example, in + // + // resources :posts do + // get "timestamp", to: :timestamp + // end + // + // The route is GET /posts/:post_id/timestamp => posts/timestamp + not exists(extractController(this.getActionString())) and + exists(ResourcesRoute r | + r.getDefiningMethodCall() = parentBlock.(ResourcesRouteBlock).getDefiningMethodCall() + | + result = r.getLastControllerComponent() + ) + ) + } + + private string getActionString() { + method.getKeywordArgument("to").getConstantValue().isStringlikeValue(result) + or + method.getKeywordArgument("to").(MethodCall).getMethodName() = "redirect" and + result = "#" + } + + override string getAction() { + // get "/photos", action: "index" + method.getKeywordArgument("action").getConstantValue().isStringlikeValue(result) + or + not exists(method.getKeywordArgument("action")) and + ( + // get "/photos", to: "photos#index" + // get "/photos", to: redirect("some_url") + result = extractAction(this.getActionString()) + or + // resources :photos, only: [] do + // get "/", to: "index" + // end + not exists(extractAction(this.getActionString())) and result = this.getActionString() + or + // get :some_action + not exists(this.getActionString()) and + method.getArgument(0).getConstantValue().isStringlikeValue(result) + ) + } + + override string getHttpMethod() { result = method.getMethodName().toString() } + } + + /** + * A route generated by a call to `resources`. + * + * ```ruby + * resources :photos + * ``` + * This creates eight routes, equivalent to the following code: + * ```ruby + * get "/photos", to: "photos#index" + * get "/photos/new", to: "photos#new" + * post "/photos", to: "photos#create" + * get "/photos/:id", to: "photos#show" + * get "/photos/:id/edit", to: "photos#edit" + * patch "/photos/:id", to: "photos#update" + * put "/photos/:id", to: "photos#update" + * delete "/photos/:id", to: "photos#delete" + * ``` + * + * `resources` can take a block. Any routes defined inside the block will inherit a path component of + * `//:_id`. For example: + * + * ```ruby + * resources :photos do + * get "/foo", to: "photos#foo" + * end + * ``` + * This creates the eight default routes, plus one more, which is nested under "/photos/:photo_id", equivalent to: + * ```ruby + * get "/photos/:photo_id/foo", to: "photos#foo" + * ``` + */ + private class ResourcesRoute extends RouteImpl, TResourcesRoute { + RouteBlock parent; + string action; + string httpMethod; + string pathComponent; + + ResourcesRoute() { + exists(string resource | + this = TResourcesRoute(parent, method, action) and + method.getArgument(0).getConstantValue().isStringlikeValue(resource) and + isDefaultResourceRoute(resource, httpMethod, pathComponent, action) + ) + } + + override string getAPrimaryQlClass() { result = "ResourcesRoute" } + + override RouteBlock getParentBlock() { result = parent } + + override string getLastPathComponent() { result = pathComponent } + + override string getLastControllerComponent() { + method.getArgument(0).getConstantValue().isStringlikeValue(result) + } + + override string getAction() { result = action } + + override string getHttpMethod() { result = httpMethod } + } + + /** + * A route generated by a call to `resource`. + * This is like a `resources` route, but creates routes for a singular resource. + * This means there's no index route, no id parameter, and the resource name is expected to be singular. + * It will still be routed to a pluralised controller name. + * ```ruby + * resource :account + * ``` + */ + private class SingularResourceRoute extends RouteImpl, TResourceRoute { + RouteBlock parent; + string action; + string httpMethod; + string pathComponent; + + SingularResourceRoute() { + exists(string resource | + this = TResourceRoute(parent, method, action) and + method.getArgument(0).getConstantValue().isStringlikeValue(resource) and + isDefaultSingularResourceRoute(resource, httpMethod, pathComponent, action) + ) + } + + override string getAPrimaryQlClass() { result = "SingularResourceRoute" } + + override RouteBlock getParentBlock() { result = parent } + + override string getLastPathComponent() { result = pathComponent } + + override string getLastControllerComponent() { + method.getArgument(0).getConstantValue().isStringlikeValue(result) + } + + override string getAction() { result = action } + + override string getHttpMethod() { result = httpMethod } + } + + /** + * A route generated by a call to `match`. + * This is a lower level primitive that powers `get`, `post` etc. + * The first argument can be a path or a (path, controller-action) pair. + * The controller, action and HTTP method can be specified with the + * `controller:`, `action:` and `via:` keyword arguments, respectively. + * ```ruby + * match 'photos/:id' => 'photos#show', via: :get + * match 'photos/:id', to: 'photos#show', via: :get + * match 'photos/:id', to 'photos#show', via: [:get, :post] + * match 'photos/:id', controller: 'photos', action: 'show', via: :get + * ``` + */ + private class MatchRoute extends RouteImpl, TMatchRoute { + private RouteBlock parent; + + MatchRoute() { this = TMatchRoute(parent, method) } + + override string getAPrimaryQlClass() { result = "MatchRoute" } + + override RouteBlock getParentBlock() { result = parent } + + override string getLastPathComponent() { + [method.getArgument(0), method.getArgument(0).(Pair).getKey()] + .getConstantValue() + .isStringlikeValue(result) + } + + override string getLastControllerComponent() { + result = + extractController(method.getKeywordArgument("to").getConstantValue().getStringlikeValue()) or + method.getKeywordArgument("controller").getConstantValue().isStringlikeValue(result) or + result = + extractController(method + .getArgument(0) + .(Pair) + .getValue() + .getConstantValue() + .getStringlikeValue()) + } + + override string getHttpMethod() { + exists(string via | + method.getKeywordArgument("via").getConstantValue().isStringlikeValue(via) + | + via = "all" and result = anyHttpMethod() + or + via != "all" and result = via + ) + or + result = + method + .getKeywordArgument("via") + .(ArrayLiteral) + .getElement(_) + .getConstantValue() + .getStringlikeValue() + } + + override string getAction() { + result = + extractAction(method.getKeywordArgument("to").getConstantValue().getStringlikeValue()) or + method.getKeywordArgument("action").getConstantValue().isStringlikeValue(result) or + result = + extractAction(method + .getArgument(0) + .(Pair) + .getValue() + .getConstantValue() + .getStringlikeValue()) + } + } + + private import Cached + + /** + * This module contains the IPA types backing `RouteBlock` and `Route`, cached for performance. */ cached - newtype TRoute = + private module Cached { + cached + newtype TRouteBlock = + TTopLevelRouteBlock(MethodCall routes, MethodCall draw, Block b) { + routes.getMethodName() = "routes" and + draw.getMethodName() = "draw" and + draw.getReceiver() = routes and + draw.getBlock() = b + } or + // constraints(foo: /some_regex/) do + // get "/posts/:foo", to "posts#something" + // end + TConstraintsRouteBlock(RouteBlock parent, MethodCall constraints, Block b) { + parent.getAStmt() = constraints and + constraints.getMethodName() = "constraints" and + constraints.getBlock() = b + } or + // scope(path: "/some_path", module: "some_module") do + // get "/posts/:foo", to "posts#something" + // end + TScopeRouteBlock(RouteBlock parent, MethodCall scope, Block b) { + parent.getAStmt() = scope and scope.getMethodName() = "scope" and scope.getBlock() = b + } or + // resources :articles do + // get "/comments", to "comments#index" + // end + TResourcesRouteBlock(RouteBlock parent, MethodCall resources, Block b) { + parent.getAStmt() = resources and + resources.getMethodName() = "resources" and + resources.getBlock() = b + } or + // A conditional statement guarding some routes. + // We ignore the condition and analyze both branches to obtain as + // much routing information as possible. + TConditionalRouteBlock(RouteBlock parent, ConditionalExpr e) { parent.getAStmt() = e } or + // namespace :admin do + // resources :posts + // end + TNamespaceRouteBlock(RouteBlock parent, MethodCall namespace, Block b) { + parent.getAStmt() = namespace and + namespace.getMethodName() = "namespace" and + namespace.getBlock() = b + } + /** - * See `ExplicitRoute` + * A route configuration. See `Route` for more info */ - TExplicitRoute(RouteBlock b, MethodCall m) { - b.getAStmt() = m and m.getMethodName() = anyHttpMethod() - } or - /** - * See `ResourcesRoute` - */ - TResourcesRoute(RouteBlock b, MethodCall m, string action) { - b.getAStmt() = m and - m.getMethodName() = "resources" and - action in ["show", "index", "new", "edit", "create", "update", "destroy"] and - applyActionFilters(m, action) - } or - /** - * See `SingularResourceRoute` - */ - TResourceRoute(RouteBlock b, MethodCall m, string action) { - b.getAStmt() = m and - m.getMethodName() = "resource" and - action in ["show", "new", "edit", "create", "update", "destroy"] and - applyActionFilters(m, action) - } or - /** - * See `MatchRoute` - */ - TMatchRoute(RouteBlock b, MethodCall m) { b.getAStmt() = m and m.getMethodName() = "match" } - } + cached + newtype TRoute = + /** + * See `ExplicitRoute` + */ + TExplicitRoute(RouteBlock b, MethodCall m) { + b.getAStmt() = m and m.getMethodName() = anyHttpMethod() + } or + /** + * See `ResourcesRoute` + */ + TResourcesRoute(RouteBlock b, MethodCall m, string action) { + b.getAStmt() = m and + m.getMethodName() = "resources" and + action in ["show", "index", "new", "edit", "create", "update", "destroy"] and + applyActionFilters(m, action) + } or + /** + * See `SingularResourceRoute` + */ + TResourceRoute(RouteBlock b, MethodCall m, string action) { + b.getAStmt() = m and + m.getMethodName() = "resource" and + action in ["show", "new", "edit", "create", "update", "destroy"] and + applyActionFilters(m, action) + } or + /** + * See `MatchRoute` + */ + TMatchRoute(RouteBlock b, MethodCall m) { b.getAStmt() = m and m.getMethodName() = "match" } + } - /** - * Several routing methods support the keyword arguments `only:` and `except:`. - * - `only:` restricts the set of actions to just those in the argument. - * - `except:` removes the given actions from the set. - */ - bindingset[action] - private predicate applyActionFilters(MethodCall m, string action) { - // Respect the `only` keyword argument, which restricts the set of actions. - ( - not exists(m.getKeywordArgument("only")) - or - exists(Expr only | only = m.getKeywordArgument("only") | - [only.(ArrayLiteral).getElement(_), only].getConstantValue().isStringlikeValue(action) - ) - ) and - // Respect the `except` keyword argument, which removes actions from the default set. - ( - not exists(m.getKeywordArgument("except")) - or - exists(Expr except | except = m.getKeywordArgument("except") | - [except.(ArrayLiteral).getElement(_), except].getConstantValue().getStringlikeValue() != - action - ) - ) - } - - /** - * Holds if the (resource, method, path, action) combination would be generated by a call to `resources :`. - */ - bindingset[resource] - private predicate isDefaultResourceRoute( - string resource, string method, string path, string action - ) { - action = "create" and - (method = "post" and path = "/" + resource) - or - action = "index" and - (method = "get" and path = "/" + resource) - or - action = "new" and - (method = "get" and path = "/" + resource + "/new") - or - action = "edit" and - (method = "get" and path = "/" + resource + ":id/edit") - or - action = "show" and - (method = "get" and path = "/" + resource + "/:id") - or - action = "update" and - (method in ["put", "patch"] and path = "/" + resource + "/:id") - or - action = "destroy" and - (method = "delete" and path = "/" + resource + "/:id") - } - - /** - * Holds if the (resource, method, path, action) combination would be generated by a call to `resource :`. - */ - bindingset[resource] - private predicate isDefaultSingularResourceRoute( - string resource, string method, string path, string action - ) { - action = "create" and - (method = "post" and path = "/" + resource) - or - action = "new" and - (method = "get" and path = "/" + resource + "/new") - or - action = "edit" and - (method = "get" and path = "/" + resource + "/edit") - or - action = "show" and - (method = "get" and path = "/" + resource) - or - action = "update" and - (method in ["put", "patch"] and path = "/" + resource) - or - action = "destroy" and - (method = "delete" and path = "/" + resource) - } - - /** - * Extract the controller from a Rails routing string - * ``` - * extractController("posts#show") = "posts" - * ``` - */ - bindingset[input] - private string extractController(string input) { result = input.regexpCapture("([^#]+)#.+", 1) } - - /** - * Extract the action from a Rails routing string - * ``` - * extractAction("posts#show") = "show" - */ - bindingset[input] - private string extractAction(string input) { result = input.regexpCapture("[^#]+#(.+)", 1) } - - /** - * Returns the lowercase name of every HTTP method we support. - */ - private string anyHttpMethod() { result = ["get", "post", "put", "patch", "delete"] } - - /** - * The inverse of `pluralize` - * photos => photo - * stories => story - * not_plural => not_plural - */ - bindingset[input] - private string singularize(string input) { - exists(string prefix | prefix = input.regexpCapture("(.*)ies", 1) | result = prefix + "y") - or - not input.matches("%ies") and - exists(string prefix | prefix = input.regexpCapture("(.*)s", 1) | result = prefix) - or - not input.regexpMatch(".*(ies|s)") and result = input - } - - /** - * Convert a camel-case string to underscore case. Converts `::` to `/`. - * This can be used to convert ActiveRecord controller names to a canonical form that matches the routes they handle. - * Note: All-uppercase words like `CONSTANT` are not handled correctly. - */ - bindingset[base] - string underscore(string base) { - base = "" and result = "" - or - result = - base.charAt(0).toLowerCase() + - // Walk along the string, keeping track of the previous character - // in order to determine if we've crossed a boundary. - // Boundaries are: - // - lower case to upper case: B in FooBar - // - entering a namespace: B in Foo::Bar - concat(int i, string prev, string char | - prev = base.charAt(i) and - char = base.charAt(i + 1) - | - any(string s | - char.regexpMatch("[A-Za-z0-9]") and - if prev = ":" - then s = "/" + char.toLowerCase() - else - if prev.isLowercase() and char.isUppercase() - then s = "_" + char.toLowerCase() - else s = char.toLowerCase() - ) - order by - i + /** + * Several routing methods support the keyword arguments `only:` and `except:`. + * - `only:` restricts the set of actions to just those in the argument. + * - `except:` removes the given actions from the set. + */ + bindingset[action] + private predicate applyActionFilters(MethodCall m, string action) { + // Respect the `only` keyword argument, which restricts the set of actions. + ( + not exists(m.getKeywordArgument("only")) + or + exists(Expr only | only = m.getKeywordArgument("only") | + [only.(ArrayLiteral).getElement(_), only].getConstantValue().isStringlikeValue(action) ) - } + ) and + // Respect the `except` keyword argument, which removes actions from the default set. + ( + not exists(m.getKeywordArgument("except")) + or + exists(Expr except | except = m.getKeywordArgument("except") | + [except.(ArrayLiteral).getElement(_), except].getConstantValue().getStringlikeValue() != + action + ) + ) + } - /** - * Strip leading and trailing forward slashes from the string. - */ - bindingset[input] - private string stripSlashes(string input) { - result = input.regexpReplaceAll("^/+(.+)$", "$1").regexpReplaceAll("^(.*[^/])/+$", "$1") + /** + * Holds if the (resource, method, path, action) combination would be generated by a call to `resources :`. + */ + bindingset[resource] + private predicate isDefaultResourceRoute( + string resource, string method, string path, string action + ) { + action = "create" and + (method = "post" and path = "/" + resource) + or + action = "index" and + (method = "get" and path = "/" + resource) + or + action = "new" and + (method = "get" and path = "/" + resource + "/new") + or + action = "edit" and + (method = "get" and path = "/" + resource + ":id/edit") + or + action = "show" and + (method = "get" and path = "/" + resource + "/:id") + or + action = "update" and + (method in ["put", "patch"] and path = "/" + resource + "/:id") + or + action = "destroy" and + (method = "delete" and path = "/" + resource + "/:id") + } + + /** + * Holds if the (resource, method, path, action) combination would be generated by a call to `resource :`. + */ + bindingset[resource] + private predicate isDefaultSingularResourceRoute( + string resource, string method, string path, string action + ) { + action = "create" and + (method = "post" and path = "/" + resource) + or + action = "new" and + (method = "get" and path = "/" + resource + "/new") + or + action = "edit" and + (method = "get" and path = "/" + resource + "/edit") + or + action = "show" and + (method = "get" and path = "/" + resource) + or + action = "update" and + (method in ["put", "patch"] and path = "/" + resource) + or + action = "destroy" and + (method = "delete" and path = "/" + resource) + } + + /** + * Extract the controller from a Rails routing string + * ``` + * extractController("posts#show") = "posts" + * ``` + */ + bindingset[input] + private string extractController(string input) { result = input.regexpCapture("([^#]+)#.+", 1) } + + /** + * Extract the action from a Rails routing string + * ``` + * extractAction("posts#show") = "show" + */ + bindingset[input] + private string extractAction(string input) { result = input.regexpCapture("[^#]+#(.+)", 1) } + + /** + * Returns the lowercase name of every HTTP method we support. + */ + private string anyHttpMethod() { result = ["get", "post", "put", "patch", "delete"] } + + /** + * The inverse of `pluralize` + * photos => photo + * stories => story + * not_plural => not_plural + */ + bindingset[input] + private string singularize(string input) { + exists(string prefix | prefix = input.regexpCapture("(.*)ies", 1) | result = prefix + "y") + or + not input.matches("%ies") and + exists(string prefix | prefix = input.regexpCapture("(.*)s", 1) | result = prefix) + or + not input.regexpMatch(".*(ies|s)") and result = input + } + + /** + * Convert a camel-case string to underscore case. Converts `::` to `/`. + * This can be used to convert ActiveRecord controller names to a canonical form that matches the routes they handle. + * Note: All-uppercase words like `CONSTANT` are not handled correctly. + */ + bindingset[base] + string underscore(string base) { + base = "" and result = "" + or + result = + base.charAt(0).toLowerCase() + + // Walk along the string, keeping track of the previous character + // in order to determine if we've crossed a boundary. + // Boundaries are: + // - lower case to upper case: B in FooBar + // - entering a namespace: B in Foo::Bar + concat(int i, string prev, string char | + prev = base.charAt(i) and + char = base.charAt(i + 1) + | + any(string s | + char.regexpMatch("[A-Za-z0-9]") and + if prev = ":" + then s = "/" + char.toLowerCase() + else + if prev.isLowercase() and char.isUppercase() + then s = "_" + char.toLowerCase() + else s = char.toLowerCase() + ) + order by + i + ) + } + + /** + * Strip leading and trailing forward slashes from the string. + */ + bindingset[input] + private string stripSlashes(string input) { + result = input.regexpReplaceAll("^/+(.+)$", "$1").regexpReplaceAll("^(.*[^/])/+$", "$1") + } } } diff --git a/ruby/ql/test/library-tests/frameworks/ActionDispatch.ql b/ruby/ql/test/library-tests/frameworks/ActionDispatch.ql index a78d18629f0..47d903a2e4d 100644 --- a/ruby/ql/test/library-tests/frameworks/ActionDispatch.ql +++ b/ruby/ql/test/library-tests/frameworks/ActionDispatch.ql @@ -3,7 +3,7 @@ private import codeql.ruby.frameworks.ActionDispatch private import codeql.ruby.frameworks.ActionController query predicate actionDispatchRoutes( - ActionDispatch::Route r, string method, string path, string controller, string action + ActionDispatch::Routing::Route r, string method, string path, string controller, string action ) { r.getHttpMethod() = method and r.getPath() = path and @@ -12,13 +12,13 @@ query predicate actionDispatchRoutes( } query predicate actionDispatchControllerMethods( - ActionDispatch::Route r, ActionControllerActionMethod m + ActionDispatch::Routing::Route r, ActionControllerActionMethod m ) { m.getARoute() = r } query predicate underscore(string input, string output) { - output = ActionDispatch::underscore(input) and + output = ActionDispatch::Routing::underscore(input) and input in [ "Foo", "FooBar", "Foo::Bar", "FooBar::Baz", "Foo::Bar::Baz", "Foo::Bar::BazQuux", "invalid", "HTTPServerRequest", "LotsOfCapitalLetters" From b7be25e18f3020df4d469e2b566dff45608a75c4 Mon Sep 17 00:00:00 2001 From: Harry Maclean Date: Fri, 29 Jul 2022 11:39:41 +1200 Subject: [PATCH 358/505] Ruby: Make isInterpretedAsRegExp extensible This allows frameworks to add new instances where a node is interpreted as a regular expression. We introduce a class RegExpInterpretation::Range that represents these nodes. In the future we may want to make this a full Concept, but it's not necessary at the moment. --- ruby/ql/lib/codeql/ruby/Regexp.qll | 38 ++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/Regexp.qll b/ruby/ql/lib/codeql/ruby/Regexp.qll index b6872d2bca3..60ed9b16641 100644 --- a/ruby/ql/lib/codeql/ruby/Regexp.qll +++ b/ruby/ql/lib/codeql/ruby/Regexp.qll @@ -92,20 +92,32 @@ private class ParsedStringRegExp extends RegExp { override string getFlags() { none() } } +/** Provides a class for modelling regular expression interpretations. */ +module RegExpInterpretation { + /** + * Nodes that are not regular expression literals, but are used in places that + * may interpret them regular expressions. Typically these are strings that flow + * to method calls like `RegExp.new`. + */ + abstract class Range extends DataFlow::Node { } +} + /** - * Holds if `source` may be interpreted as a regular expression. + * Nodes interpreted as regular expressions via various standard library methods. */ -private predicate isInterpretedAsRegExp(DataFlow::Node source) { - // The first argument to an invocation of `Regexp.new` or `Regexp.compile`. - source = API::getTopLevelMember("Regexp").getAMethodCall(["compile", "new"]).getArgument(0) - or - // The argument of a call that coerces the argument to a regular expression. - exists(DataFlow::CallNode mce | - mce.getMethodName() = ["match", "match?"] and - source = mce.getArgument(0) and - // exclude https://ruby-doc.org/core-2.4.0/Regexp.html#method-i-match - not mce.getReceiver().asExpr().getExpr() instanceof AST::RegExpLiteral - ) +class StdLibRegExpInterpretation extends RegExpInterpretation::Range { + StdLibRegExpInterpretation() { + // The first argument to an invocation of `Regexp.new` or `Regexp.compile`. + this = API::getTopLevelMember("Regexp").getAMethodCall(["compile", "new"]).getArgument(0) + or + // The argument of a call that coerces the argument to a regular expression. + exists(DataFlow::CallNode mce | + mce.getMethodName() = ["match", "match?"] and + this = mce.getArgument(0) and + // exclude https://ruby-doc.org/core-2.4.0/Regexp.html#method-i-match + not mce.getReceiver().asExpr().getExpr() instanceof AST::RegExpLiteral + ) + } } private class RegExpConfiguration extends Configuration { @@ -120,7 +132,7 @@ private class RegExpConfiguration extends Configuration { ) } - override predicate isSink(DataFlow::Node sink) { isInterpretedAsRegExp(sink) } + override predicate isSink(DataFlow::Node sink) { sink instanceof RegExpInterpretation::Range } override predicate isSanitizer(DataFlow::Node node) { // stop flow if `node` is receiver of From f42d33312fc77f6340902515c1a406b4a2bcb002 Mon Sep 17 00:00:00 2001 From: Harry Maclean Date: Fri, 29 Jul 2022 11:41:48 +1200 Subject: [PATCH 359/505] Ruby: Model Mime::Type Add type summaries to recognise instances of Mime::Type, and recognise arguments to Mime::Type.match? and Mime::Type.=~ as regular expression interpretations. --- .../codeql/ruby/frameworks/ActionDispatch.qll | 43 +++++++++++++++++++ .../frameworks/ActionDispatch.expected | 12 ++++++ .../frameworks/ActionDispatch.ql | 14 ++++++ .../frameworks/action_dispatch/mime_type.rb | 14 ++++++ 4 files changed, 83 insertions(+) create mode 100644 ruby/ql/test/library-tests/frameworks/action_dispatch/mime_type.rb diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActionDispatch.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActionDispatch.qll index 35a198ef7f2..a3a83a49ecc 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/ActionDispatch.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActionDispatch.qll @@ -6,12 +6,55 @@ private import codeql.ruby.AST private import codeql.ruby.Concepts private import codeql.ruby.DataFlow +private import codeql.ruby.Regexp as RE +private import codeql.ruby.dataflow.FlowSummary +private import codeql.ruby.frameworks.data.ModelsAsData /** * Models the `ActionDispatch` HTTP library, which is part of Rails. * Version: 7.1.0. */ module ActionDispatch { + /** + * Type summaries for the `Mime::Type` class, i.e. method calls that produce new + * `Mime::Type` instances. + */ + private class MimeTypeTypeSummary extends ModelInput::TypeModelCsv { + override predicate row(string row) { + // package1;type1;package2;type2;path + row = + [ + // Mime[type] : Mime::Type (omitted) + // Method names with brackets like [] cannot be represented in MaD. + // Mime.fetch(type) : Mime::Type + "actiondispatch;Mime::Type;;;Member[Mime].Method[fetch].ReturnValue", + // Mime::Type.new(str) : Mime::Type + "actiondispatch;Mime::Type;;;Member[Mime].Member[Type].Instance", + // Mime::Type.lookup(str) : Mime::Type + "actiondispatch;Mime::Type;;;Member[Mime].Member[Type].Method[lookup].ReturnValue", + // Mime::Type.lookup_by_extension(str) : Mime::Type + "actiondispatch;Mime::Type;;;Member[Mime].Member[Type].Method[lookup_by_extension].ReturnValue", + // Mime::Type.register(str) : Mime::Type + "actiondispatch;Mime::Type;;;Member[Mime].Member[Type].Method[register].ReturnValue", + // Mime::Type.register_alias(str) : Mime::Type + "actiondispatch;Mime::Type;;;Member[Mime].Member[Type].Method[register_alias].ReturnValue", + ] + } + } + + /** + * An argument to `Mime::Type#match?`, which is converted to a RegExp via + * `Regexp.new`. + */ + class MimeTypeMatchRegExpInterpretation extends RE::RegExpInterpretation::Range { + MimeTypeMatchRegExpInterpretation() { + this = + ModelOutput::getATypeNode("actiondispatch", "Mime::Type") + .getAMethodCall(["match?", "=~"]) + .getArgument(0) + } + } + /** * Models routing configuration specified using the `ActionDispatch` library, which is part of Rails. */ diff --git a/ruby/ql/test/library-tests/frameworks/ActionDispatch.expected b/ruby/ql/test/library-tests/frameworks/ActionDispatch.expected index 5ea30e67680..1593bb24374 100644 --- a/ruby/ql/test/library-tests/frameworks/ActionDispatch.expected +++ b/ruby/ql/test/library-tests/frameworks/ActionDispatch.expected @@ -54,3 +54,15 @@ underscore | HTTPServerRequest | httpserver_request | | LotsOfCapitalLetters | lots_of_capital_letters | | invalid | invalid | +mimeTypeInstances +| action_dispatch/mime_type.rb:2:6:2:28 | Use getMember("Mime").getMethod("fetch").getReturn() | +| action_dispatch/mime_type.rb:3:6:3:32 | Use getMember("Mime").getMember("Type").getMethod("new").getReturn() | +| action_dispatch/mime_type.rb:4:6:4:35 | Use getMember("Mime").getMember("Type").getMethod("lookup").getReturn() | +| action_dispatch/mime_type.rb:5:6:5:43 | Use getMember("Mime").getMember("Type").getMethod("lookup_by_extension").getReturn() | +| action_dispatch/mime_type.rb:6:6:6:47 | Use getMember("Mime").getMember("Type").getMethod("register").getReturn() | +| action_dispatch/mime_type.rb:7:6:7:64 | Use getMember("Mime").getMember("Type").getMethod("register_alias").getReturn() | +mimeTypeMatchRegExpInterpretations +| action_dispatch/mime_type.rb:11:11:11:19 | "foo/bar" | +| action_dispatch/mime_type.rb:12:7:12:15 | "foo/bar" | +| action_dispatch/mime_type.rb:13:11:13:11 | s | +| action_dispatch/mime_type.rb:14:7:14:7 | s | diff --git a/ruby/ql/test/library-tests/frameworks/ActionDispatch.ql b/ruby/ql/test/library-tests/frameworks/ActionDispatch.ql index 47d903a2e4d..f2f65f58523 100644 --- a/ruby/ql/test/library-tests/frameworks/ActionDispatch.ql +++ b/ruby/ql/test/library-tests/frameworks/ActionDispatch.ql @@ -1,6 +1,10 @@ private import ruby private import codeql.ruby.frameworks.ActionDispatch private import codeql.ruby.frameworks.ActionController +private import codeql.ruby.ApiGraphs +private import codeql.ruby.frameworks.data.ModelsAsData +private import codeql.ruby.DataFlow +private import codeql.ruby.Regexp as RE query predicate actionDispatchRoutes( ActionDispatch::Routing::Route r, string method, string path, string controller, string action @@ -24,3 +28,13 @@ query predicate underscore(string input, string output) { "HTTPServerRequest", "LotsOfCapitalLetters" ] } + +query predicate mimeTypeInstances(API::Node n) { + n = ModelOutput::getATypeNode("actiondispatch", "Mime::Type") +} + +query predicate mimeTypeMatchRegExpInterpretations( + ActionDispatch::MimeTypeMatchRegExpInterpretation s +) { + any() +} diff --git a/ruby/ql/test/library-tests/frameworks/action_dispatch/mime_type.rb b/ruby/ql/test/library-tests/frameworks/action_dispatch/mime_type.rb new file mode 100644 index 00000000000..9f37682bb2b --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/action_dispatch/mime_type.rb @@ -0,0 +1,14 @@ +m1 = Mime["text/html"] # not recognised due to MaD limitation (can't parse method name) +m2 = Mime.fetch("text/html") +m3 = Mime::Type.new("text/html") +m4 = Mime::Type.lookup("text/html") +m5 = Mime::Type.lookup_by_extension("jpeg") +m6 = Mime::Type.register("text/calendar", :ics) +m7 = Mime::Type.register_alias("application/xml", :opf, %w(opf)) + +s = "foo/bar" + +m2.match? "foo/bar" +m3 =~ "foo/bar" +m4.match? s +m5 =~ s From 20344986906b37782f7c6a1d0b6b89c59b7ff4a7 Mon Sep 17 00:00:00 2001 From: Harry Maclean Date: Fri, 29 Jul 2022 12:20:32 +1200 Subject: [PATCH 360/505] Ruby: Fix QLDoc warnings --- ruby/ql/lib/codeql/ruby/Regexp.qll | 8 ++++---- .../ql/lib/codeql/ruby/frameworks/ActionDispatch.qll | 12 ++++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/Regexp.qll b/ruby/ql/lib/codeql/ruby/Regexp.qll index 60ed9b16641..23237c28b0b 100644 --- a/ruby/ql/lib/codeql/ruby/Regexp.qll +++ b/ruby/ql/lib/codeql/ruby/Regexp.qll @@ -92,12 +92,12 @@ private class ParsedStringRegExp extends RegExp { override string getFlags() { none() } } -/** Provides a class for modelling regular expression interpretations. */ +/** Provides a class for modeling regular expression interpretations. */ module RegExpInterpretation { /** - * Nodes that are not regular expression literals, but are used in places that - * may interpret them regular expressions. Typically these are strings that flow - * to method calls like `RegExp.new`. + * A node that is not a regular expression literal, but is used in places that + * may interpret it as one. Instances of this class are typically strings that + * flow to method calls like `RegExp.new`. */ abstract class Range extends DataFlow::Node { } } diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActionDispatch.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActionDispatch.qll index a3a83a49ecc..e9ff9d5c86a 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/ActionDispatch.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActionDispatch.qll @@ -953,10 +953,14 @@ module ActionDispatch { private string anyHttpMethod() { result = ["get", "post", "put", "patch", "delete"] } /** - * The inverse of `pluralize` - * photos => photo - * stories => story - * not_plural => not_plural + * The inverse of `pluralize`. If `input` is a plural word, it returns the + * singular version. + * + * Examples: + * + * - photos -> photo + * - stories -> story + * - not_plural -> not_plural */ bindingset[input] private string singularize(string input) { From c4283dd23f8de6f3a1501090f5a83e65cb3cb253 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Tue, 26 Jul 2022 11:31:50 +0200 Subject: [PATCH 361/505] C++: Support `__is_assignable` builtin While here fix the documentation of `__is_trivially_assignable` and `__is_nothrow_assignable`. --- .../code/cpp/exprs/BuiltInOperations.qll | 29 ++++++++++++++----- cpp/ql/lib/semmlecode.cpp.dbscheme | 2 ++ .../builtins/type_traits/expr.expected | 9 ++++++ .../library-tests/builtins/type_traits/ms.cpp | 5 +++- 4 files changed, 36 insertions(+), 9 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/exprs/BuiltInOperations.qll b/cpp/ql/lib/semmle/code/cpp/exprs/BuiltInOperations.qll index 309d98cd694..c794b574045 100644 --- a/cpp/ql/lib/semmle/code/cpp/exprs/BuiltInOperations.qll +++ b/cpp/ql/lib/semmle/code/cpp/exprs/BuiltInOperations.qll @@ -612,13 +612,10 @@ class BuiltInOperationIsTriviallyDestructible extends BuiltInOperation, @istrivi * The `__is_trivially_assignable` built-in operation (used by some * implementations of the `` header). * - * Returns `true` if the assignment operator `C::operator =(const C& c)` is - * trivial. + * Returns `true` if the assignment operator `C::operator =(const D& d)` is + * trivial (i.e., it will not call any operation that is non-trivial). * ``` - * template - * struct is_trivially_assignable - * : public integral_constant - * { }; + * bool v = __is_trivially_assignable(MyType1, MyType2); * ``` */ class BuiltInOperationIsTriviallyAssignable extends BuiltInOperation, @istriviallyassignableexpr { @@ -631,10 +628,10 @@ class BuiltInOperationIsTriviallyAssignable extends BuiltInOperation, @istrivial * The `__is_nothrow_assignable` built-in operation (used by some * implementations of the `` header). * - * Returns true if there exists a `C::operator =(const C& c) nothrow` + * Returns true if there exists a `C::operator =(const D& d) nothrow` * assignment operator (i.e, with an empty exception specification). * ``` - * bool v = __is_nothrow_assignable(MyType); + * bool v = __is_nothrow_assignable(MyType1, MyType2); * ``` */ class BuiltInOperationIsNothrowAssignable extends BuiltInOperation, @isnothrowassignableexpr { @@ -643,6 +640,22 @@ class BuiltInOperationIsNothrowAssignable extends BuiltInOperation, @isnothrowas override string getAPrimaryQlClass() { result = "BuiltInOperationIsNothrowAssignable" } } +/** + * The `__is_assignable` built-in operation (used by some implementations + * of the `` header). + * + * Returns true if there exists a `C::operator =(const D& d)` assignment + * operator. + * ``` + * bool v = __is_assignable(MyType1, MyType2); + * ``` + */ +class BuiltInOperationIsAssignable extends BuiltInOperation, @isassignable { + override string toString() { result = "__is_assignable" } + + override string getAPrimaryQlClass() { result = "BuiltInOperationIsAssignable" } +} + /** * The `__is_standard_layout` built-in operation (used by some implementations * of the `` header). diff --git a/cpp/ql/lib/semmlecode.cpp.dbscheme b/cpp/ql/lib/semmlecode.cpp.dbscheme index 19e31bf071f..f214ac4d338 100644 --- a/cpp/ql/lib/semmlecode.cpp.dbscheme +++ b/cpp/ql/lib/semmlecode.cpp.dbscheme @@ -1650,6 +1650,7 @@ case @expr.kind of | 327 = @co_await | 328 = @co_yield | 329 = @temp_init +| 330 = @isassignable ; @var_args_expr = @vastartexpr @@ -1711,6 +1712,7 @@ case @expr.kind of | @isfinalexpr | @builtinchooseexpr | @builtincomplex + | @isassignable ; new_allocated_type( diff --git a/cpp/ql/test/library-tests/builtins/type_traits/expr.expected b/cpp/ql/test/library-tests/builtins/type_traits/expr.expected index 47918496198..4d59a3341f0 100644 --- a/cpp/ql/test/library-tests/builtins/type_traits/expr.expected +++ b/cpp/ql/test/library-tests/builtins/type_traits/expr.expected @@ -296,3 +296,12 @@ | ms.cpp:255:24:255:43 | a_struct | | | | ms.cpp:256:24:256:49 | __is_final | a_final_struct | 1 | | ms.cpp:256:24:256:49 | a_final_struct | | | +| ms.cpp:258:29:258:62 | __is_assignable | a_struct,a_struct | 1 | +| ms.cpp:258:29:258:62 | a_struct | | | +| ms.cpp:258:29:258:62 | a_struct | | | +| ms.cpp:259:29:259:59 | __is_assignable | a_struct,empty | 0 | +| ms.cpp:259:29:259:59 | a_struct | | | +| ms.cpp:259:29:259:59 | empty | | | +| ms.cpp:260:29:260:57 | __is_assignable | a_struct,int | 0 | +| ms.cpp:260:29:260:57 | a_struct | | | +| ms.cpp:260:29:260:57 | int | | | diff --git a/cpp/ql/test/library-tests/builtins/type_traits/ms.cpp b/cpp/ql/test/library-tests/builtins/type_traits/ms.cpp index 742f95faf07..aacd74e578a 100644 --- a/cpp/ql/test/library-tests/builtins/type_traits/ms.cpp +++ b/cpp/ql/test/library-tests/builtins/type_traits/ms.cpp @@ -254,5 +254,8 @@ void f(void) { bool b_is_final1 = __is_final(a_struct); bool b_is_final2 = __is_final(a_final_struct); -} + bool b_is_assignable1 = __is_assignable(a_struct,a_struct); + bool b_is_assignable2 = __is_assignable(a_struct,empty); + bool b_is_assignable3 = __is_assignable(a_struct,int); +} From 0c039354373f7005fe1aa7269ee861aa2978d241 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Tue, 26 Jul 2022 12:34:47 +0200 Subject: [PATCH 362/505] C++: Support `__is_aggregate` builtin Fix some whitespace issues while here. --- .../code/cpp/exprs/BuiltInOperations.qll | 36 +++++++++++++------ cpp/ql/lib/semmlecode.cpp.dbscheme | 2 ++ .../builtins/type_traits/expr.expected | 4 +++ .../library-tests/builtins/type_traits/ms.cpp | 3 ++ 4 files changed, 34 insertions(+), 11 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/exprs/BuiltInOperations.qll b/cpp/ql/lib/semmle/code/cpp/exprs/BuiltInOperations.qll index c794b574045..44550e13335 100644 --- a/cpp/ql/lib/semmle/code/cpp/exprs/BuiltInOperations.qll +++ b/cpp/ql/lib/semmle/code/cpp/exprs/BuiltInOperations.qll @@ -173,7 +173,7 @@ class BuiltInOperationHasAssign extends BuiltInOperation, @hasassignexpr { * * Returns `true` if the type has a copy constructor. * ``` - * std::integral_constant< bool, __has_copy(_Tp)> hc; + * std::integral_constant hc; * ``` */ class BuiltInOperationHasCopy extends BuiltInOperation, @hascopyexpr { @@ -189,7 +189,7 @@ class BuiltInOperationHasCopy extends BuiltInOperation, @hascopyexpr { * Returns `true` if a copy assignment operator has an empty exception * specification. * ``` - * std::integral_constant< bool, __has_nothrow_assign(_Tp)> hnta; + * std::integral_constant hnta; * ``` */ class BuiltInOperationHasNoThrowAssign extends BuiltInOperation, @hasnothrowassign { @@ -220,7 +220,7 @@ class BuiltInOperationHasNoThrowConstructor extends BuiltInOperation, @hasnothro * * Returns `true` if the copy constructor has an empty exception specification. * ``` - * std::integral_constant< bool, __has_nothrow_copy(MyType) >; + * std::integral_constant; * ``` */ class BuiltInOperationHasNoThrowCopy extends BuiltInOperation, @hasnothrowcopy { @@ -266,7 +266,7 @@ class BuiltInOperationHasTrivialConstructor extends BuiltInOperation, @hastrivia * * Returns true if the type has a trivial copy constructor. * ``` - * std::integral_constant< bool, __has_trivial_copy(MyType) > htc; + * std::integral_constant htc; * ``` */ class BuiltInOperationHasTrivialCopy extends BuiltInOperation, @hastrivialcopy { @@ -468,7 +468,7 @@ class BuiltInOperationIsUnion extends BuiltInOperation, @isunionexpr { * ``` * template * struct types_compatible - * : public integral_constant + * : public integral_constant * { }; * ``` */ @@ -547,7 +547,7 @@ class BuiltInOperationBuiltInAddressOf extends UnaryOperation, BuiltInOperation, * ``` * template * struct is_trivially_constructible - * : public integral_constant + * : public integral_constant * { }; * ``` */ @@ -701,7 +701,7 @@ class BuiltInOperationIsTriviallyCopyable extends BuiltInOperation, @istrivially * * ``` * template - * std::integral_constant< bool, __is_literal_type(_Tp)> ilt; + * std::integral_constant ilt; * ``` */ class BuiltInOperationIsLiteralType extends BuiltInOperation, @isliteraltypeexpr { @@ -718,7 +718,7 @@ class BuiltInOperationIsLiteralType extends BuiltInOperation, @isliteraltypeexpr * compiler, with semantics of the `memcpy` operation. * ``` * template - * std::integral_constant< bool, __has_trivial_move_constructor(_Tp)> htmc; + * std::integral_constant htmc; * ``` */ class BuiltInOperationHasTrivialMoveConstructor extends BuiltInOperation, @@ -736,7 +736,7 @@ class BuiltInOperationHasTrivialMoveConstructor extends BuiltInOperation, * ``` * template * struct has_trivial_move_assign - * : public integral_constant + * : public integral_constant * { }; * ``` */ @@ -771,7 +771,7 @@ class BuiltInOperationHasNothrowMoveAssign extends BuiltInOperation, @hasnothrow * ``` * template * struct is_constructible - * : public integral_constant + * : public integral_constant * { }; * ``` */ @@ -935,7 +935,7 @@ class BuiltInOperationIsValueClass extends BuiltInOperation, @isvalueclassexpr { * ``` * template * struct is_final - * : public integral_constant + * : public integral_constant * { }; * ``` */ @@ -991,3 +991,17 @@ class BuiltInComplexOperation extends BuiltInOperation, @builtincomplex { /** Gets the operand corresponding to the imaginary part of the complex number. */ Expr getImaginaryOperand() { this.hasChild(result, 1) } } + +/** + * A C++ `__is_aggregate` built-in operation (used by some implementations of the + * `` header). + * + * Returns `true` if the type has is an aggregate type. + * ``` + * std::integral_constant ia; + * ``` */ +class BuiltInOperationIsAggregate extends BuiltInOperation, @isaggregate { + override string toString() { result = "__is_aggregate" } + + override string getAPrimaryQlClass() { result = "BuiltInOperationIsAggregate" } +} diff --git a/cpp/ql/lib/semmlecode.cpp.dbscheme b/cpp/ql/lib/semmlecode.cpp.dbscheme index f214ac4d338..d438935e5ef 100644 --- a/cpp/ql/lib/semmlecode.cpp.dbscheme +++ b/cpp/ql/lib/semmlecode.cpp.dbscheme @@ -1651,6 +1651,7 @@ case @expr.kind of | 328 = @co_yield | 329 = @temp_init | 330 = @isassignable +| 331 = @isaggregate ; @var_args_expr = @vastartexpr @@ -1713,6 +1714,7 @@ case @expr.kind of | @builtinchooseexpr | @builtincomplex | @isassignable + | @isaggregate ; new_allocated_type( diff --git a/cpp/ql/test/library-tests/builtins/type_traits/expr.expected b/cpp/ql/test/library-tests/builtins/type_traits/expr.expected index 4d59a3341f0..10db02f8450 100644 --- a/cpp/ql/test/library-tests/builtins/type_traits/expr.expected +++ b/cpp/ql/test/library-tests/builtins/type_traits/expr.expected @@ -305,3 +305,7 @@ | ms.cpp:260:29:260:57 | __is_assignable | a_struct,int | 0 | | ms.cpp:260:29:260:57 | a_struct | | | | ms.cpp:260:29:260:57 | int | | | +| ms.cpp:262:28:262:51 | __is_aggregate | a_struct | 1 | +| ms.cpp:262:28:262:51 | a_struct | | | +| ms.cpp:263:28:263:46 | __is_aggregate | int | 0 | +| ms.cpp:263:28:263:46 | int | | | diff --git a/cpp/ql/test/library-tests/builtins/type_traits/ms.cpp b/cpp/ql/test/library-tests/builtins/type_traits/ms.cpp index aacd74e578a..6d8ea3c6161 100644 --- a/cpp/ql/test/library-tests/builtins/type_traits/ms.cpp +++ b/cpp/ql/test/library-tests/builtins/type_traits/ms.cpp @@ -258,4 +258,7 @@ void f(void) { bool b_is_assignable1 = __is_assignable(a_struct,a_struct); bool b_is_assignable2 = __is_assignable(a_struct,empty); bool b_is_assignable3 = __is_assignable(a_struct,int); + + bool b_is_aggregate1 = __is_aggregate(a_struct); + bool b_is_aggregate2 = __is_aggregate(int); } From a85d3f9b7f09656b327d8f7a44e24a4f7399a9b3 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Tue, 26 Jul 2022 12:46:35 +0200 Subject: [PATCH 363/505] C++: Support `__has_unique_object_representations` builtin --- .../code/cpp/exprs/BuiltInOperations.qll | 20 ++++++++++++++++++- cpp/ql/lib/semmlecode.cpp.dbscheme | 2 ++ .../builtins/type_traits/expr.expected | 4 ++++ .../library-tests/builtins/type_traits/ms.cpp | 3 +++ 4 files changed, 28 insertions(+), 1 deletion(-) diff --git a/cpp/ql/lib/semmle/code/cpp/exprs/BuiltInOperations.qll b/cpp/ql/lib/semmle/code/cpp/exprs/BuiltInOperations.qll index 44550e13335..cd361d0ac8f 100644 --- a/cpp/ql/lib/semmle/code/cpp/exprs/BuiltInOperations.qll +++ b/cpp/ql/lib/semmle/code/cpp/exprs/BuiltInOperations.qll @@ -999,9 +999,27 @@ class BuiltInComplexOperation extends BuiltInOperation, @builtincomplex { * Returns `true` if the type has is an aggregate type. * ``` * std::integral_constant ia; - * ``` */ + * ``` + */ class BuiltInOperationIsAggregate extends BuiltInOperation, @isaggregate { override string toString() { result = "__is_aggregate" } override string getAPrimaryQlClass() { result = "BuiltInOperationIsAggregate" } } + +/** + * A C++ `__has_unique_object_representations` built-in operation (used by some + * implementations of the `` header). + * + * Returns `true` if the type is trivially copyable and if the object representation + * is unique for two objects with the same value. + * ``` + * bool v = __has_unique_object_representations(MyType); + * ``` + */ +class BuiltInOperationHasUniqueObjectRepresentations extends BuiltInOperation, + @hasuniqueobjectrepresentations { + override string toString() { result = "__has_unique_object_representations" } + + override string getAPrimaryQlClass() { result = "BuiltInOperationHasUniqueObjectRepresentations" } +} diff --git a/cpp/ql/lib/semmlecode.cpp.dbscheme b/cpp/ql/lib/semmlecode.cpp.dbscheme index d438935e5ef..7fb82ddb3e3 100644 --- a/cpp/ql/lib/semmlecode.cpp.dbscheme +++ b/cpp/ql/lib/semmlecode.cpp.dbscheme @@ -1652,6 +1652,7 @@ case @expr.kind of | 329 = @temp_init | 330 = @isassignable | 331 = @isaggregate +| 332 = @hasuniqueobjectrepresentations ; @var_args_expr = @vastartexpr @@ -1715,6 +1716,7 @@ case @expr.kind of | @builtincomplex | @isassignable | @isaggregate + | @hasuniqueobjectrepresentations ; new_allocated_type( diff --git a/cpp/ql/test/library-tests/builtins/type_traits/expr.expected b/cpp/ql/test/library-tests/builtins/type_traits/expr.expected index 10db02f8450..a19d917aaac 100644 --- a/cpp/ql/test/library-tests/builtins/type_traits/expr.expected +++ b/cpp/ql/test/library-tests/builtins/type_traits/expr.expected @@ -309,3 +309,7 @@ | ms.cpp:262:28:262:51 | a_struct | | | | ms.cpp:263:28:263:46 | __is_aggregate | int | 0 | | ms.cpp:263:28:263:46 | int | | | +| ms.cpp:265:49:265:88 | __has_unique_object_representations | int | 1 | +| ms.cpp:265:49:265:88 | int | | | +| ms.cpp:266:49:266:90 | __has_unique_object_representations | float | 0 | +| ms.cpp:266:49:266:90 | float | | | diff --git a/cpp/ql/test/library-tests/builtins/type_traits/ms.cpp b/cpp/ql/test/library-tests/builtins/type_traits/ms.cpp index 6d8ea3c6161..91d6245cc35 100644 --- a/cpp/ql/test/library-tests/builtins/type_traits/ms.cpp +++ b/cpp/ql/test/library-tests/builtins/type_traits/ms.cpp @@ -261,4 +261,7 @@ void f(void) { bool b_is_aggregate1 = __is_aggregate(a_struct); bool b_is_aggregate2 = __is_aggregate(int); + + bool b_has_unique_object_representations1 = __has_unique_object_representations(int); + bool b_has_unique_object_representations2 = __has_unique_object_representations(float); } From 81e687ea9854334ca8e85340033c664e91221043 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Tue, 26 Jul 2022 13:12:57 +0200 Subject: [PATCH 364/505] C++: Support `__builtin_bit_cast` builtin --- .../semmle/code/cpp/exprs/BuiltInOperations.qll | 15 +++++++++++++++ cpp/ql/lib/semmlecode.cpp.dbscheme | 2 ++ cpp/ql/test/library-tests/builtins/edg/edg.c | 5 ++++- .../test/library-tests/builtins/edg/expr.expected | 3 +++ 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/cpp/ql/lib/semmle/code/cpp/exprs/BuiltInOperations.qll b/cpp/ql/lib/semmle/code/cpp/exprs/BuiltInOperations.qll index cd361d0ac8f..6e413c64d7f 100644 --- a/cpp/ql/lib/semmle/code/cpp/exprs/BuiltInOperations.qll +++ b/cpp/ql/lib/semmle/code/cpp/exprs/BuiltInOperations.qll @@ -1023,3 +1023,18 @@ class BuiltInOperationHasUniqueObjectRepresentations extends BuiltInOperation, override string getAPrimaryQlClass() { result = "BuiltInOperationHasUniqueObjectRepresentations" } } + +/** + * A C/C++ `__builtin_bit_cast` built-in operation (used by some implementations + * of `std::bit_cast`). + * + * Performs a bit cast from a value to a type. + * ``` + * __builtin_bit_cast(Type, value); + * ``` + */ +class BuiltInBitCast extends BuiltInOperation, @builtinbitcast { + override string toString() { result = "__builtin_bit_cast" } + + override string getAPrimaryQlClass() { result = "BuiltInBitCast" } +} diff --git a/cpp/ql/lib/semmlecode.cpp.dbscheme b/cpp/ql/lib/semmlecode.cpp.dbscheme index 7fb82ddb3e3..f350bfc257f 100644 --- a/cpp/ql/lib/semmlecode.cpp.dbscheme +++ b/cpp/ql/lib/semmlecode.cpp.dbscheme @@ -1653,6 +1653,7 @@ case @expr.kind of | 330 = @isassignable | 331 = @isaggregate | 332 = @hasuniqueobjectrepresentations +| 333 = @builtinbitcast ; @var_args_expr = @vastartexpr @@ -1717,6 +1718,7 @@ case @expr.kind of | @isassignable | @isaggregate | @hasuniqueobjectrepresentations + | @builtinbitcast ; new_allocated_type( diff --git a/cpp/ql/test/library-tests/builtins/edg/edg.c b/cpp/ql/test/library-tests/builtins/edg/edg.c index d1a78435317..f1f0f0f1375 100644 --- a/cpp/ql/test/library-tests/builtins/edg/edg.c +++ b/cpp/ql/test/library-tests/builtins/edg/edg.c @@ -1,4 +1,4 @@ - +// semmle-extractor-options: --clang struct mystruct { int f1; int f2; @@ -13,3 +13,6 @@ void f(void) { int i2 = edg_offsetof(struct mystruct,f2); } +void g(void) { + double f = __builtin_bit_cast(double,42l); +} diff --git a/cpp/ql/test/library-tests/builtins/edg/expr.expected b/cpp/ql/test/library-tests/builtins/edg/expr.expected index 0969dc1e217..5ab0747ecc7 100644 --- a/cpp/ql/test/library-tests/builtins/edg/expr.expected +++ b/cpp/ql/test/library-tests/builtins/edg/expr.expected @@ -13,3 +13,6 @@ | edg.c:13:14:13:45 | (size_t)... | 0 | 0 | | edg.c:13:14:13:45 | __INTADDR__ | 1 | 1 | | edg.c:13:43:13:44 | f2 | 0 | 0 | +| edg.c:17:16:17:45 | __builtin_bit_cast | 1 | 1 | +| edg.c:17:16:17:45 | double | 0 | 0 | +| edg.c:17:42:17:44 | 42 | 1 | 1 | From 20b66eaf34fc2fa2b972610c39ac4b6afcb4fba9 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Tue, 26 Jul 2022 13:59:30 +0200 Subject: [PATCH 365/505] C++: Support `__builtin_shuffle` builtin While here write gcc instead of GNU, which is more accurate. --- .../code/cpp/exprs/BuiltInOperations.qll | 21 +++++++++++++++++-- cpp/ql/lib/semmlecode.cpp.dbscheme | 2 ++ .../vector_types/builtin_ops.expected | 2 ++ .../vector_types/variables.expected | 6 ++++++ .../vector_types/vector_types2.cpp | 12 +++++++++++ 5 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 cpp/ql/test/library-tests/vector_types/vector_types2.cpp diff --git a/cpp/ql/lib/semmle/code/cpp/exprs/BuiltInOperations.qll b/cpp/ql/lib/semmle/code/cpp/exprs/BuiltInOperations.qll index 6e413c64d7f..866ed3c314a 100644 --- a/cpp/ql/lib/semmle/code/cpp/exprs/BuiltInOperations.qll +++ b/cpp/ql/lib/semmle/code/cpp/exprs/BuiltInOperations.qll @@ -121,7 +121,7 @@ class BuiltInNoOp extends BuiltInOperation, @noopexpr { /** * A C/C++ `__builtin_offsetof` built-in operation (used by some implementations * of `offsetof`). The operation retains its semantics even in the presence - * of an overloaded `operator &`). This is a GNU/Clang extension. + * of an overloaded `operator &`). This is a gcc/clang extension. * ``` * struct S { * int a, b; @@ -494,6 +494,23 @@ class BuiltInOperationBuiltInShuffleVector extends BuiltInOperation, @builtinshu override string getAPrimaryQlClass() { result = "BuiltInOperationBuiltInShuffleVector" } } +/** + * A gcc `__builtin_shuffle` expression. + * + * It outputs a permutation of elements from one or two input vectors. + * Please see https://gcc.gnu.org/onlinedocs/gcc/Vector-Extensions.html + * for more information. + * ``` + * // Concatenate every other element of 4-element vectors V1 and V2. + * V3 = __builtin_shufflevector(V1, V2, {0, 2, 4, 6}); + * ``` + */ +class BuiltInOperationBuiltInShuffle extends BuiltInOperation, @builtinshuffle { + override string toString() { result = "__builtin_shuffle" } + + override string getAPrimaryQlClass() { result = "BuiltInOperationBuiltInShuffle" } +} + /** * A clang `__builtin_convertvector` expression. * @@ -946,7 +963,7 @@ class BuiltInOperationIsFinal extends BuiltInOperation, @isfinalexpr { } /** - * The `__builtin_choose_expr` expression. This is a GNU/Clang extension. + * The `__builtin_choose_expr` expression. This is a gcc/clang extension. * * The expression functions similarly to the ternary `?:` operator, except * that it is evaluated at compile-time. diff --git a/cpp/ql/lib/semmlecode.cpp.dbscheme b/cpp/ql/lib/semmlecode.cpp.dbscheme index f350bfc257f..23f7cbb88a4 100644 --- a/cpp/ql/lib/semmlecode.cpp.dbscheme +++ b/cpp/ql/lib/semmlecode.cpp.dbscheme @@ -1654,6 +1654,7 @@ case @expr.kind of | 331 = @isaggregate | 332 = @hasuniqueobjectrepresentations | 333 = @builtinbitcast +| 334 = @builtinshuffle ; @var_args_expr = @vastartexpr @@ -1719,6 +1720,7 @@ case @expr.kind of | @isaggregate | @hasuniqueobjectrepresentations | @builtinbitcast + | @builtinshuffle ; new_allocated_type( diff --git a/cpp/ql/test/library-tests/vector_types/builtin_ops.expected b/cpp/ql/test/library-tests/vector_types/builtin_ops.expected index 2c0dd9d0017..756ca2f1c17 100644 --- a/cpp/ql/test/library-tests/vector_types/builtin_ops.expected +++ b/cpp/ql/test/library-tests/vector_types/builtin_ops.expected @@ -1,2 +1,4 @@ +| vector_types2.cpp:10:15:10:42 | __builtin_shuffle | +| vector_types2.cpp:11:15:11:45 | __builtin_shuffle | | vector_types.cpp:31:13:31:49 | __builtin_shufflevector | | vector_types.cpp:58:10:58:52 | __builtin_convertvector | diff --git a/cpp/ql/test/library-tests/vector_types/variables.expected b/cpp/ql/test/library-tests/vector_types/variables.expected index 2494f192e9a..52fa98dd3f0 100644 --- a/cpp/ql/test/library-tests/vector_types/variables.expected +++ b/cpp/ql/test/library-tests/vector_types/variables.expected @@ -13,6 +13,12 @@ | file://:0:0:0:0 | gp_offset | gp_offset | file://:0:0:0:0 | unsigned int | 4 | | file://:0:0:0:0 | overflow_arg_area | overflow_arg_area | file://:0:0:0:0 | void * | 8 | | file://:0:0:0:0 | reg_save_area | reg_save_area | file://:0:0:0:0 | void * | 8 | +| vector_types2.cpp:5:7:5:7 | a | a | vector_types2.cpp:2:13:2:15 | v4i | 16 | +| vector_types2.cpp:6:7:6:7 | b | b | vector_types2.cpp:2:13:2:15 | v4i | 16 | +| vector_types2.cpp:7:7:7:12 | mask_1 | mask_1 | vector_types2.cpp:2:13:2:15 | v4i | 16 | +| vector_types2.cpp:8:7:8:12 | mask_2 | mask_2 | vector_types2.cpp:2:13:2:15 | v4i | 16 | +| vector_types2.cpp:10:7:10:11 | res_1 | res_1 | vector_types2.cpp:2:13:2:15 | v4i | 16 | +| vector_types2.cpp:11:7:11:11 | res_2 | res_2 | vector_types2.cpp:2:13:2:15 | v4i | 16 | | vector_types.cpp:9:21:9:21 | x | x | vector_types.cpp:6:15:6:17 | v4f | 16 | | vector_types.cpp:14:18:14:20 | lhs | lhs | vector_types.cpp:6:15:6:17 | v4f | 16 | | vector_types.cpp:14:27:14:29 | rhs | rhs | vector_types.cpp:6:15:6:17 | v4f | 16 | diff --git a/cpp/ql/test/library-tests/vector_types/vector_types2.cpp b/cpp/ql/test/library-tests/vector_types/vector_types2.cpp new file mode 100644 index 00000000000..d4233b28890 --- /dev/null +++ b/cpp/ql/test/library-tests/vector_types/vector_types2.cpp @@ -0,0 +1,12 @@ +// semmle-extractor-options: --gnu --gnu_version 80000 +typedef int v4i __attribute__((vector_size (16))); + +void f() { + v4i a = {1,2,3,4}; + v4i b = {5,6,7,8}; + v4i mask_1 = {3,0,1,2}; + v4i mask_2 = {3,5,4,2}; + + v4i res_1 = __builtin_shuffle(a, mask_1); + v4i res_2 = __builtin_shuffle(a, b, mask_2); +} From 1806b8933f9271ebb47a7ee9783f19aece21e3d1 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Tue, 26 Jul 2022 16:26:40 +0200 Subject: [PATCH 366/505] C++: Add change note for newly added builtins --- .../lib/change-notes/2022-07-26-additional-builtin-support.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 cpp/ql/lib/change-notes/2022-07-26-additional-builtin-support.md diff --git a/cpp/ql/lib/change-notes/2022-07-26-additional-builtin-support.md b/cpp/ql/lib/change-notes/2022-07-26-additional-builtin-support.md new file mode 100644 index 00000000000..2e4d7db69a5 --- /dev/null +++ b/cpp/ql/lib/change-notes/2022-07-26-additional-builtin-support.md @@ -0,0 +1,4 @@ +--- +category: feature +--- +* Added subclasses of `BuiltInOperations` for `__builtin_bit_cast`, `__builtin_shuffle`, `__has_unique_object_representations`, `__is_aggregate`, and `__is_assignable`. From 295ecbb4016253db5943ea25a65f038ba81fd53f Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Tue, 26 Jul 2022 16:53:34 +0200 Subject: [PATCH 367/505] C++: Add upgrade and downgrade scripts for new builtins --- .../exprs.ql | 17 + .../old.dbscheme | 2125 +++++++++++++++++ .../semmlecode.cpp.dbscheme | 2115 ++++++++++++++++ .../upgrade.properties | 3 + .../old.dbscheme | 2115 ++++++++++++++++ .../semmlecode.cpp.dbscheme | 2125 +++++++++++++++++ .../upgrade.properties | 2 + 7 files changed, 8502 insertions(+) create mode 100644 cpp/downgrades/23f7cbb88a4eb29f30c3490363dc201bc054c5ff/exprs.ql create mode 100644 cpp/downgrades/23f7cbb88a4eb29f30c3490363dc201bc054c5ff/old.dbscheme create mode 100644 cpp/downgrades/23f7cbb88a4eb29f30c3490363dc201bc054c5ff/semmlecode.cpp.dbscheme create mode 100644 cpp/downgrades/23f7cbb88a4eb29f30c3490363dc201bc054c5ff/upgrade.properties create mode 100644 cpp/ql/lib/upgrades/19e31bf071f588bb7efd1e4d5a185ce4f6fbbd84/old.dbscheme create mode 100644 cpp/ql/lib/upgrades/19e31bf071f588bb7efd1e4d5a185ce4f6fbbd84/semmlecode.cpp.dbscheme create mode 100644 cpp/ql/lib/upgrades/19e31bf071f588bb7efd1e4d5a185ce4f6fbbd84/upgrade.properties diff --git a/cpp/downgrades/23f7cbb88a4eb29f30c3490363dc201bc054c5ff/exprs.ql b/cpp/downgrades/23f7cbb88a4eb29f30c3490363dc201bc054c5ff/exprs.ql new file mode 100644 index 00000000000..d00685e7cc6 --- /dev/null +++ b/cpp/downgrades/23f7cbb88a4eb29f30c3490363dc201bc054c5ff/exprs.ql @@ -0,0 +1,17 @@ +class Expr extends @expr { + string toString() { none() } +} + +class Location extends @location_expr { + string toString() { none() } +} + +predicate isExprWithNewBuiltin(Expr expr) { + exists(int kind | exprs(expr, kind, _) | 330 <= kind and kind <= 334) +} + +from Expr expr, int kind, int kind_new, Location location +where + exprs(expr, kind, location) and + if isExprWithNewBuiltin(expr) then kind_new = 0 else kind_new = kind +select expr, kind_new, location diff --git a/cpp/downgrades/23f7cbb88a4eb29f30c3490363dc201bc054c5ff/old.dbscheme b/cpp/downgrades/23f7cbb88a4eb29f30c3490363dc201bc054c5ff/old.dbscheme new file mode 100644 index 00000000000..23f7cbb88a4 --- /dev/null +++ b/cpp/downgrades/23f7cbb88a4eb29f30c3490363dc201bc054c5ff/old.dbscheme @@ -0,0 +1,2125 @@ + +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * gcc -c f1.c f2.c f3.c + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + /** + * An invocation of the compiler. Note that more than one file may + * be compiled per invocation. For example, this command compiles + * three source files: + * + * gcc -c f1.c f2.c f3.c + */ + unique int id : @compilation, + string cwd : string ref +); + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * gcc -c f1.c f2.c f3.c + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | *path to extractor* + * 1 | `--mimic` + * 2 | `/usr/bin/gcc` + * 3 | `-c` + * 4 | f1.c + * 5 | f2.c + * 6 | f3.c + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * gcc -c f1.c f2.c f3.c + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | f1.c + * 1 | f2.c + * 2 | f3.c + * + * Note that even if those files `#include` headers, those headers + * do not appear as rows. + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + + +/** + * External data, loaded from CSV files during snapshot creation. See + * [Tutorial: Incorporating external data](https://help.semmle.com/wiki/display/SD/Tutorial%3A+Incorporating+external+data) + * for more information. + */ +externalData( + int id : @externalDataElement, + string path : string ref, + int column: int ref, + string value : string ref +); + +/** + * The source location of the snapshot. + */ +sourceLocationPrefix(string prefix : string ref); + +/** + * Information about packages that provide code used during compilation. + * The `id` is just a unique identifier. + * The `namespace` is typically the name of the package manager that + * provided the package (e.g. "dpkg" or "yum"). + * The `package_name` is the name of the package, and `version` is its + * version (as a string). + */ +external_packages( + unique int id: @external_package, + string namespace : string ref, + string package_name : string ref, + string version : string ref +); + +/** + * Holds if File `fileid` was provided by package `package`. + */ +header_to_external_package( + int fileid : @file ref, + int package : @external_package ref +); + +/* + * Version history + */ + +svnentries( + unique int id : @svnentry, + string revision : string ref, + string author : string ref, + date revisionDate : date ref, + int changeSize : int ref +) + +svnaffectedfiles( + int id : @svnentry ref, + int file : @file ref, + string action : string ref +) + +svnentrymsg( + unique int id : @svnentry ref, + string message : string ref +) + +svnchurn( + int commit : @svnentry ref, + int file : @file ref, + int addedLines : int ref, + int deletedLines : int ref +) + +/* + * C++ dbscheme + */ + +@location = @location_stmt | @location_expr | @location_default ; + +/** + * The location of an element that is not an expression or a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ +locations_default( + /** The location of an element that is not an expression or a statement. */ + unique int id: @location_default, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ +locations_stmt( + /** The location of a statement. */ + unique int id: @location_stmt, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of an expression. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ +locations_expr( + /** The location of an expression. */ + unique int id: @location_expr, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** An element for which line-count information is available. */ +@sourceline = @file | @function | @variable | @enumconstant | @xmllocatable; + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +files( + unique int id: @file, + string name: string ref +); + +folders( + unique int id: @folder, + string name: string ref +); + +@container = @folder | @file + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +fileannotations( + int id: @file ref, + int kind: int ref, + string name: string ref, + string value: string ref +); + +inmacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +affectedbymacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +/* + case @macroinvocations.kind of + 1 = macro expansion + | 2 = other macro reference + ; +*/ +macroinvocations( + unique int id: @macroinvocation, + int macro_id: @ppd_define ref, + int location: @location_default ref, + int kind: int ref +); + +macroparent( + unique int id: @macroinvocation ref, + int parent_id: @macroinvocation ref +); + +// a macroinvocation may be part of another location +// the way to find a constant expression that uses a macro +// is thus to find a constant expression that has a location +// to which a macro invocation is bound +macrolocationbind( + int id: @macroinvocation ref, + int location: @location ref +); + +#keyset[invocation, argument_index] +macro_argument_unexpanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +#keyset[invocation, argument_index] +macro_argument_expanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +/* + case @function.kind of + 1 = normal + | 2 = constructor + | 3 = destructor + | 4 = conversion + | 5 = operator + | 6 = builtin // GCC built-in functions, e.g. __builtin___memcpy_chk + ; +*/ +functions( + unique int id: @function, + string name: string ref, + int kind: int ref +); + +function_entry_point(int id: @function ref, unique int entry_point: @stmt ref); + +function_return_type(int id: @function ref, int return_type: @type ref); + +/** If `function` is a coroutine, then this gives the + std::experimental::resumable_traits instance associated with it, + and the variables representing the `handle` and `promise` for it. */ +coroutine( + unique int function: @function ref, + int traits: @type ref, + int handle: @variable ref, + int promise: @variable ref +); + +/** The `new` function used for allocating the coroutine state, if any. */ +coroutine_new( + unique int function: @function ref, + int new: @function ref +); + +/** The `delete` function used for deallocating the coroutine state, if any. */ +coroutine_delete( + unique int function: @function ref, + int delete: @function ref +); + +purefunctions(unique int id: @function ref); + +function_deleted(unique int id: @function ref); + +function_defaulted(unique int id: @function ref); + +member_function_this_type(unique int id: @function ref, int this_type: @type ref); + +#keyset[id, type_id] +fun_decls( + int id: @fun_decl, + int function: @function ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +fun_def(unique int id: @fun_decl ref); +fun_specialized(unique int id: @fun_decl ref); +fun_implicit(unique int id: @fun_decl ref); +fun_decl_specifiers( + int id: @fun_decl ref, + string name: string ref +) +#keyset[fun_decl, index] +fun_decl_throws( + int fun_decl: @fun_decl ref, + int index: int ref, + int type_id: @type ref +); +/* an empty throw specification is different from none */ +fun_decl_empty_throws(unique int fun_decl: @fun_decl ref); +fun_decl_noexcept( + int fun_decl: @fun_decl ref, + int constant: @expr ref +); +fun_decl_empty_noexcept(int fun_decl: @fun_decl ref); +fun_decl_typedef_type( + unique int fun_decl: @fun_decl ref, + int typedeftype_id: @usertype ref +); + +param_decl_bind( + unique int id: @var_decl ref, + int index: int ref, + int fun_decl: @fun_decl ref +); + +#keyset[id, type_id] +var_decls( + int id: @var_decl, + int variable: @variable ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +var_def(unique int id: @var_decl ref); +var_decl_specifiers( + int id: @var_decl ref, + string name: string ref +) +is_structured_binding(unique int id: @variable ref); + +type_decls( + unique int id: @type_decl, + int type_id: @type ref, + int location: @location_default ref +); +type_def(unique int id: @type_decl ref); +type_decl_top( + unique int type_decl: @type_decl ref +); + +namespace_decls( + unique int id: @namespace_decl, + int namespace_id: @namespace ref, + int location: @location_default ref, + int bodylocation: @location_default ref +); + +usings( + unique int id: @using, + int element_id: @element ref, + int location: @location_default ref +); + +/** The element which contains the `using` declaration. */ +using_container( + int parent: @element ref, + int child: @using ref +); + +static_asserts( + unique int id: @static_assert, + int condition : @expr ref, + string message : string ref, + int location: @location_default ref, + int enclosing : @element ref +); + +// each function has an ordered list of parameters +#keyset[id, type_id] +#keyset[function, index, type_id] +params( + int id: @parameter, + int function: @functionorblock ref, + int index: int ref, + int type_id: @type ref +); + +overrides(int new: @function ref, int old: @function ref); + +#keyset[id, type_id] +membervariables( + int id: @membervariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +globalvariables( + int id: @globalvariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +localvariables( + int id: @localvariable, + int type_id: @type ref, + string name: string ref +); + +autoderivation( + unique int var: @variable ref, + int derivation_type: @type ref +); + +enumconstants( + unique int id: @enumconstant, + int parent: @usertype ref, + int index: int ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); + +@variable = @localscopevariable | @globalvariable | @membervariable; + +@localscopevariable = @localvariable | @parameter; + +/* + Built-in types are the fundamental types, e.g., integral, floating, and void. + + case @builtintype.kind of + 1 = error + | 2 = unknown + | 3 = void + | 4 = boolean + | 5 = char + | 6 = unsigned_char + | 7 = signed_char + | 8 = short + | 9 = unsigned_short + | 10 = signed_short + | 11 = int + | 12 = unsigned_int + | 13 = signed_int + | 14 = long + | 15 = unsigned_long + | 16 = signed_long + | 17 = long_long + | 18 = unsigned_long_long + | 19 = signed_long_long + | 20 = __int8 // Microsoft-specific + | 21 = __int16 // Microsoft-specific + | 22 = __int32 // Microsoft-specific + | 23 = __int64 // Microsoft-specific + | 24 = float + | 25 = double + | 26 = long_double + | 27 = _Complex_float // C99-specific + | 28 = _Complex_double // C99-specific + | 29 = _Complex_long double // C99-specific + | 30 = _Imaginary_float // C99-specific + | 31 = _Imaginary_double // C99-specific + | 32 = _Imaginary_long_double // C99-specific + | 33 = wchar_t // Microsoft-specific + | 34 = decltype_nullptr // C++11 + | 35 = __int128 + | 36 = unsigned___int128 + | 37 = signed___int128 + | 38 = __float128 + | 39 = _Complex___float128 + | 40 = _Decimal32 + | 41 = _Decimal64 + | 42 = _Decimal128 + | 43 = char16_t + | 44 = char32_t + | 45 = _Float32 + | 46 = _Float32x + | 47 = _Float64 + | 48 = _Float64x + | 49 = _Float128 + | 50 = _Float128x + | 51 = char8_t + ; +*/ +builtintypes( + unique int id: @builtintype, + string name: string ref, + int kind: int ref, + int size: int ref, + int sign: int ref, + int alignment: int ref +); + +/* + Derived types are types that are directly derived from existing types and + point to, refer to, transform type data to return a new type. + + case @derivedtype.kind of + 1 = pointer + | 2 = reference + | 3 = type_with_specifiers + | 4 = array + | 5 = gnu_vector + | 6 = routineptr + | 7 = routinereference + | 8 = rvalue_reference // C++11 +// ... 9 type_conforming_to_protocols deprecated + | 10 = block + ; +*/ +derivedtypes( + unique int id: @derivedtype, + string name: string ref, + int kind: int ref, + int type_id: @type ref +); + +pointerishsize(unique int id: @derivedtype ref, + int size: int ref, + int alignment: int ref); + +arraysizes( + unique int id: @derivedtype ref, + int num_elements: int ref, + int bytesize: int ref, + int alignment: int ref +); + +typedefbase( + unique int id: @usertype ref, + int type_id: @type ref +); + +/** + * An instance of the C++11 `decltype` operator. For example: + * ``` + * int a; + * decltype(1+a) b; + * ``` + * Here `expr` is `1+a`. + * + * Sometimes an additional pair of parentheses around the expression + * would change the semantics of this decltype, e.g. + * ``` + * struct A { double x; }; + * const A* a = new A(); + * decltype( a->x ); // type is double + * decltype((a->x)); // type is const double& + * ``` + * (Please consult the C++11 standard for more details). + * `parentheses_would_change_meaning` is `true` iff that is the case. + */ +#keyset[id, expr] +decltypes( + int id: @decltype, + int expr: @expr ref, + int base_type: @type ref, + boolean parentheses_would_change_meaning: boolean ref +); + +/* + case @usertype.kind of + 1 = struct + | 2 = class + | 3 = union + | 4 = enum + | 5 = typedef // classic C: typedef typedef type name + | 6 = template + | 7 = template_parameter + | 8 = template_template_parameter + | 9 = proxy_class // a proxy class associated with a template parameter +// ... 10 objc_class deprecated +// ... 11 objc_protocol deprecated +// ... 12 objc_category deprecated + | 13 = scoped_enum + | 14 = using_alias // a using name = type style typedef + ; +*/ +usertypes( + unique int id: @usertype, + string name: string ref, + int kind: int ref +); + +usertypesize( + unique int id: @usertype ref, + int size: int ref, + int alignment: int ref +); + +usertype_final(unique int id: @usertype ref); + +usertype_uuid( + unique int id: @usertype ref, + string uuid: string ref +); + +mangled_name( + unique int id: @declaration ref, + int mangled_name : @mangledname +); + +is_pod_class(unique int id: @usertype ref); +is_standard_layout_class(unique int id: @usertype ref); + +is_complete(unique int id: @usertype ref); + +is_class_template(unique int id: @usertype ref); +class_instantiation( + int to: @usertype ref, + int from: @usertype ref +); +class_template_argument( + int type_id: @usertype ref, + int index: int ref, + int arg_type: @type ref +); +class_template_argument_value( + int type_id: @usertype ref, + int index: int ref, + int arg_value: @expr ref +); + +is_proxy_class_for( + unique int id: @usertype ref, + unique int templ_param_id: @usertype ref +); + +type_mentions( + unique int id: @type_mention, + int type_id: @type ref, + int location: @location ref, + // a_symbol_reference_kind from the EDG frontend. See symbol_ref.h there. + int kind: int ref +); + +is_function_template(unique int id: @function ref); +function_instantiation( + unique int to: @function ref, + int from: @function ref +); +function_template_argument( + int function_id: @function ref, + int index: int ref, + int arg_type: @type ref +); +function_template_argument_value( + int function_id: @function ref, + int index: int ref, + int arg_value: @expr ref +); + +is_variable_template(unique int id: @variable ref); +variable_instantiation( + unique int to: @variable ref, + int from: @variable ref +); +variable_template_argument( + int variable_id: @variable ref, + int index: int ref, + int arg_type: @type ref +); +variable_template_argument_value( + int variable_id: @variable ref, + int index: int ref, + int arg_value: @expr ref +); + +/* + Fixed point types + precision(1) = short, precision(2) = default, precision(3) = long + is_unsigned(1) = unsigned is_unsigned(2) = signed + is_fract_type(1) = declared with _Fract + saturating(1) = declared with _Sat +*/ +/* TODO +fixedpointtypes( + unique int id: @fixedpointtype, + int precision: int ref, + int is_unsigned: int ref, + int is_fract_type: int ref, + int saturating: int ref); +*/ + +routinetypes( + unique int id: @routinetype, + int return_type: @type ref +); + +routinetypeargs( + int routine: @routinetype ref, + int index: int ref, + int type_id: @type ref +); + +ptrtomembers( + unique int id: @ptrtomember, + int type_id: @type ref, + int class_id: @type ref +); + +/* + specifiers for types, functions, and variables + + "public", + "protected", + "private", + + "const", + "volatile", + "static", + + "pure", + "virtual", + "sealed", // Microsoft + "__interface", // Microsoft + "inline", + "explicit", + + "near", // near far extension + "far", // near far extension + "__ptr32", // Microsoft + "__ptr64", // Microsoft + "__sptr", // Microsoft + "__uptr", // Microsoft + "dllimport", // Microsoft + "dllexport", // Microsoft + "thread", // Microsoft + "naked", // Microsoft + "microsoft_inline", // Microsoft + "forceinline", // Microsoft + "selectany", // Microsoft + "nothrow", // Microsoft + "novtable", // Microsoft + "noreturn", // Microsoft + "noinline", // Microsoft + "noalias", // Microsoft + "restrict", // Microsoft +*/ + +specifiers( + unique int id: @specifier, + unique string str: string ref +); + +typespecifiers( + int type_id: @type ref, + int spec_id: @specifier ref +); + +funspecifiers( + int func_id: @function ref, + int spec_id: @specifier ref +); + +varspecifiers( + int var_id: @accessible ref, + int spec_id: @specifier ref +); + +attributes( + unique int id: @attribute, + int kind: int ref, + string name: string ref, + string name_space: string ref, + int location: @location_default ref +); + +case @attribute.kind of + 0 = @gnuattribute +| 1 = @stdattribute +| 2 = @declspec +| 3 = @msattribute +| 4 = @alignas +// ... 5 @objc_propertyattribute deprecated +; + +attribute_args( + unique int id: @attribute_arg, + int kind: int ref, + int attribute: @attribute ref, + int index: int ref, + int location: @location_default ref +); + +case @attribute_arg.kind of + 0 = @attribute_arg_empty +| 1 = @attribute_arg_token +| 2 = @attribute_arg_constant +| 3 = @attribute_arg_type +; + +attribute_arg_value( + unique int arg: @attribute_arg ref, + string value: string ref +); +attribute_arg_type( + unique int arg: @attribute_arg ref, + int type_id: @type ref +); +attribute_arg_name( + unique int arg: @attribute_arg ref, + string name: string ref +); + +typeattributes( + int type_id: @type ref, + int spec_id: @attribute ref +); + +funcattributes( + int func_id: @function ref, + int spec_id: @attribute ref +); + +varattributes( + int var_id: @accessible ref, + int spec_id: @attribute ref +); + +stmtattributes( + int stmt_id: @stmt ref, + int spec_id: @attribute ref +); + +@type = @builtintype + | @derivedtype + | @usertype + /* TODO | @fixedpointtype */ + | @routinetype + | @ptrtomember + | @decltype; + +unspecifiedtype( + unique int type_id: @type ref, + int unspecified_type_id: @type ref +); + +member( + int parent: @type ref, + int index: int ref, + int child: @member ref +); + +@enclosingfunction_child = @usertype | @variable | @namespace + +enclosingfunction( + unique int child: @enclosingfunction_child ref, + int parent: @function ref +); + +derivations( + unique int derivation: @derivation, + int sub: @type ref, + int index: int ref, + int super: @type ref, + int location: @location_default ref +); + +derspecifiers( + int der_id: @derivation ref, + int spec_id: @specifier ref +); + +/** + * Contains the byte offset of the base class subobject within the derived + * class. Only holds for non-virtual base classes, but see table + * `virtual_base_offsets` for offsets of virtual base class subobjects. + */ +direct_base_offsets( + unique int der_id: @derivation ref, + int offset: int ref +); + +/** + * Contains the byte offset of the virtual base class subobject for class + * `super` within a most-derived object of class `sub`. `super` can be either a + * direct or indirect base class. + */ +#keyset[sub, super] +virtual_base_offsets( + int sub: @usertype ref, + int super: @usertype ref, + int offset: int ref +); + +frienddecls( + unique int id: @frienddecl, + int type_id: @type ref, + int decl_id: @declaration ref, + int location: @location_default ref +); + +@declaredtype = @usertype ; + +@declaration = @function + | @declaredtype + | @variable + | @enumconstant + | @frienddecl; + +@member = @membervariable + | @function + | @declaredtype + | @enumconstant; + +@locatable = @diagnostic + | @declaration + | @ppd_include + | @ppd_define + | @macroinvocation + /*| @funcall*/ + | @xmllocatable + | @attribute + | @attribute_arg; + +@namedscope = @namespace | @usertype; + +@element = @locatable + | @file + | @folder + | @specifier + | @type + | @expr + | @namespace + | @initialiser + | @stmt + | @derivation + | @comment + | @preprocdirect + | @fun_decl + | @var_decl + | @type_decl + | @namespace_decl + | @using + | @namequalifier + | @specialnamequalifyingelement + | @static_assert + | @type_mention + | @lambdacapture; + +@exprparent = @element; + +comments( + unique int id: @comment, + string contents: string ref, + int location: @location_default ref +); + +commentbinding( + int id: @comment ref, + int element: @element ref +); + +exprconv( + int converted: @expr ref, + unique int conversion: @expr ref +); + +compgenerated(unique int id: @element ref); + +/** + * `destructor_call` destructs the `i`'th entity that should be + * destructed following `element`. Note that entities should be + * destructed in reverse construction order, so for a given `element` + * these should be called from highest to lowest `i`. + */ +#keyset[element, destructor_call] +#keyset[element, i] +synthetic_destructor_call( + int element: @element ref, + int i: int ref, + int destructor_call: @routineexpr ref +); + +namespaces( + unique int id: @namespace, + string name: string ref +); + +namespace_inline( + unique int id: @namespace ref +); + +namespacembrs( + int parentid: @namespace ref, + unique int memberid: @namespacembr ref +); + +@namespacembr = @declaration | @namespace; + +exprparents( + int expr_id: @expr ref, + int child_index: int ref, + int parent_id: @exprparent ref +); + +expr_isload(unique int expr_id: @expr ref); + +@cast = @c_style_cast + | @const_cast + | @dynamic_cast + | @reinterpret_cast + | @static_cast + ; + +/* +case @conversion.kind of + 0 = @simple_conversion // a numeric conversion, qualification conversion, or a reinterpret_cast +| 1 = @bool_conversion // conversion to 'bool' +| 2 = @base_class_conversion // a derived-to-base conversion +| 3 = @derived_class_conversion // a base-to-derived conversion +| 4 = @pm_base_class_conversion // a derived-to-base conversion of a pointer to member +| 5 = @pm_derived_class_conversion // a base-to-derived conversion of a pointer to member +| 6 = @glvalue_adjust // an adjustment of the type of a glvalue +| 7 = @prvalue_adjust // an adjustment of the type of a prvalue +; +*/ +/** + * Describes the semantics represented by a cast expression. This is largely + * independent of the source syntax of the cast, so it is separate from the + * regular expression kind. + */ +conversionkinds( + unique int expr_id: @cast ref, + int kind: int ref +); + +@conversion = @cast + | @array_to_pointer + | @parexpr + | @reference_to + | @ref_indirect + | @temp_init + ; + +/* +case @funbindexpr.kind of + 0 = @normal_call // a normal call +| 1 = @virtual_call // a virtual call +| 2 = @adl_call // a call whose target is only found by ADL +; +*/ +iscall(unique int caller: @funbindexpr ref, int kind: int ref); + +numtemplatearguments( + unique int expr_id: @expr ref, + int num: int ref +); + +specialnamequalifyingelements( + unique int id: @specialnamequalifyingelement, + unique string name: string ref +); + +@namequalifiableelement = @expr | @namequalifier; +@namequalifyingelement = @namespace + | @specialnamequalifyingelement + | @usertype; + +namequalifiers( + unique int id: @namequalifier, + unique int qualifiableelement: @namequalifiableelement ref, + int qualifyingelement: @namequalifyingelement ref, + int location: @location_default ref +); + +varbind( + int expr: @varbindexpr ref, + int var: @accessible ref +); + +funbind( + int expr: @funbindexpr ref, + int fun: @function ref +); + +@any_new_expr = @new_expr + | @new_array_expr; + +@new_or_delete_expr = @any_new_expr + | @delete_expr + | @delete_array_expr; + +@prefix_crement_expr = @preincrexpr | @predecrexpr; + +@postfix_crement_expr = @postincrexpr | @postdecrexpr; + +@increment_expr = @preincrexpr | @postincrexpr; + +@decrement_expr = @predecrexpr | @postdecrexpr; + +@crement_expr = @increment_expr | @decrement_expr; + +@un_arith_op_expr = @arithnegexpr + | @unaryplusexpr + | @conjugation + | @realpartexpr + | @imagpartexpr + | @crement_expr + ; + +@un_bitwise_op_expr = @complementexpr; + +@un_log_op_expr = @notexpr; + +@un_op_expr = @address_of + | @indirect + | @un_arith_op_expr + | @un_bitwise_op_expr + | @builtinaddressof + | @vec_fill + | @un_log_op_expr + | @co_await + | @co_yield + ; + +@bin_log_op_expr = @andlogicalexpr | @orlogicalexpr; + +@cmp_op_expr = @eq_op_expr | @rel_op_expr; + +@eq_op_expr = @eqexpr | @neexpr; + +@rel_op_expr = @gtexpr + | @ltexpr + | @geexpr + | @leexpr + | @spaceshipexpr + ; + +@bin_bitwise_op_expr = @lshiftexpr + | @rshiftexpr + | @andexpr + | @orexpr + | @xorexpr + ; + +@p_arith_op_expr = @paddexpr + | @psubexpr + | @pdiffexpr + ; + +@bin_arith_op_expr = @addexpr + | @subexpr + | @mulexpr + | @divexpr + | @remexpr + | @jmulexpr + | @jdivexpr + | @fjaddexpr + | @jfaddexpr + | @fjsubexpr + | @jfsubexpr + | @minexpr + | @maxexpr + | @p_arith_op_expr + ; + +@bin_op_expr = @bin_arith_op_expr + | @bin_bitwise_op_expr + | @cmp_op_expr + | @bin_log_op_expr + ; + +@op_expr = @un_op_expr + | @bin_op_expr + | @assign_expr + | @conditionalexpr + ; + +@assign_arith_expr = @assignaddexpr + | @assignsubexpr + | @assignmulexpr + | @assigndivexpr + | @assignremexpr + ; + +@assign_bitwise_expr = @assignandexpr + | @assignorexpr + | @assignxorexpr + | @assignlshiftexpr + | @assignrshiftexpr + | @assignpaddexpr + | @assignpsubexpr + ; + +@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr + +@assign_expr = @assignexpr | @assign_op_expr + +/* + case @allocator.form of + 0 = plain + | 1 = alignment + ; +*/ + +/** + * The allocator function associated with a `new` or `new[]` expression. + * The `form` column specified whether the allocation call contains an alignment + * argument. + */ +expr_allocator( + unique int expr: @any_new_expr ref, + int func: @function ref, + int form: int ref +); + +/* + case @deallocator.form of + 0 = plain + | 1 = size + | 2 = alignment + | 3 = size_and_alignment + ; +*/ + +/** + * The deallocator function associated with a `delete`, `delete[]`, `new`, or + * `new[]` expression. For a `new` or `new[]` expression, the deallocator is the + * one used to free memory if the initialization throws an exception. + * The `form` column specifies whether the deallocation call contains a size + * argument, and alignment argument, or both. + */ +expr_deallocator( + unique int expr: @new_or_delete_expr ref, + int func: @function ref, + int form: int ref +); + +/** + * Holds if the `@conditionalexpr` is of the two operand form + * `guard ? : false`. + */ +expr_cond_two_operand( + unique int cond: @conditionalexpr ref +); + +/** + * The guard of `@conditionalexpr` `guard ? true : false` + */ +expr_cond_guard( + unique int cond: @conditionalexpr ref, + int guard: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` holds. For the two operand form + * `guard ?: false` consider using `expr_cond_guard` instead. + */ +expr_cond_true( + unique int cond: @conditionalexpr ref, + int true: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` does not hold. + */ +expr_cond_false( + unique int cond: @conditionalexpr ref, + int false: @expr ref +); + +/** A string representation of the value. */ +values( + unique int id: @value, + string str: string ref +); + +/** The actual text in the source code for the value, if any. */ +valuetext( + unique int id: @value ref, + string text: string ref +); + +valuebind( + int val: @value ref, + unique int expr: @expr ref +); + +fieldoffsets( + unique int id: @variable ref, + int byteoffset: int ref, + int bitoffset: int ref +); + +bitfield( + unique int id: @variable ref, + int bits: int ref, + int declared_bits: int ref +); + +/* TODO +memberprefix( + int member: @expr ref, + int prefix: @expr ref +); +*/ + +/* + kind(1) = mbrcallexpr + kind(2) = mbrptrcallexpr + kind(3) = mbrptrmbrcallexpr + kind(4) = ptrmbrptrmbrcallexpr + kind(5) = mbrreadexpr // x.y + kind(6) = mbrptrreadexpr // p->y + kind(7) = mbrptrmbrreadexpr // x.*pm + kind(8) = mbrptrmbrptrreadexpr // x->*pm + kind(9) = staticmbrreadexpr // static x.y + kind(10) = staticmbrptrreadexpr // static p->y +*/ +/* TODO +memberaccess( + int member: @expr ref, + int kind: int ref +); +*/ + +initialisers( + unique int init: @initialiser, + int var: @accessible ref, + unique int expr: @expr ref, + int location: @location_expr ref +); + +braced_initialisers( + int init: @initialiser ref +); + +/** + * An ancestor for the expression, for cases in which we cannot + * otherwise find the expression's parent. + */ +expr_ancestor( + int exp: @expr ref, + int ancestor: @element ref +); + +exprs( + unique int id: @expr, + int kind: int ref, + int location: @location_expr ref +); + +/* + case @value.category of + 1 = prval + | 2 = xval + | 3 = lval + ; +*/ +expr_types( + int id: @expr ref, + int typeid: @type ref, + int value_category: int ref +); + +case @expr.kind of + 1 = @errorexpr +| 2 = @address_of // & AddressOfExpr +| 3 = @reference_to // ReferenceToExpr (implicit?) +| 4 = @indirect // * PointerDereferenceExpr +| 5 = @ref_indirect // ReferenceDereferenceExpr (implicit?) +// ... +| 8 = @array_to_pointer // (???) +| 9 = @vacuous_destructor_call // VacuousDestructorCall +// ... +| 11 = @assume // Microsoft +| 12 = @parexpr +| 13 = @arithnegexpr +| 14 = @unaryplusexpr +| 15 = @complementexpr +| 16 = @notexpr +| 17 = @conjugation // GNU ~ operator +| 18 = @realpartexpr // GNU __real +| 19 = @imagpartexpr // GNU __imag +| 20 = @postincrexpr +| 21 = @postdecrexpr +| 22 = @preincrexpr +| 23 = @predecrexpr +| 24 = @conditionalexpr +| 25 = @addexpr +| 26 = @subexpr +| 27 = @mulexpr +| 28 = @divexpr +| 29 = @remexpr +| 30 = @jmulexpr // C99 mul imaginary +| 31 = @jdivexpr // C99 div imaginary +| 32 = @fjaddexpr // C99 add real + imaginary +| 33 = @jfaddexpr // C99 add imaginary + real +| 34 = @fjsubexpr // C99 sub real - imaginary +| 35 = @jfsubexpr // C99 sub imaginary - real +| 36 = @paddexpr // pointer add (pointer + int or int + pointer) +| 37 = @psubexpr // pointer sub (pointer - integer) +| 38 = @pdiffexpr // difference between two pointers +| 39 = @lshiftexpr +| 40 = @rshiftexpr +| 41 = @andexpr +| 42 = @orexpr +| 43 = @xorexpr +| 44 = @eqexpr +| 45 = @neexpr +| 46 = @gtexpr +| 47 = @ltexpr +| 48 = @geexpr +| 49 = @leexpr +| 50 = @minexpr // GNU minimum +| 51 = @maxexpr // GNU maximum +| 52 = @assignexpr +| 53 = @assignaddexpr +| 54 = @assignsubexpr +| 55 = @assignmulexpr +| 56 = @assigndivexpr +| 57 = @assignremexpr +| 58 = @assignlshiftexpr +| 59 = @assignrshiftexpr +| 60 = @assignandexpr +| 61 = @assignorexpr +| 62 = @assignxorexpr +| 63 = @assignpaddexpr // assign pointer add +| 64 = @assignpsubexpr // assign pointer sub +| 65 = @andlogicalexpr +| 66 = @orlogicalexpr +| 67 = @commaexpr +| 68 = @subscriptexpr // access to member of an array, e.g., a[5] +// ... 69 @objc_subscriptexpr deprecated +// ... 70 @cmdaccess deprecated +// ... +| 73 = @virtfunptrexpr +| 74 = @callexpr +// ... 75 @msgexpr_normal deprecated +// ... 76 @msgexpr_super deprecated +// ... 77 @atselectorexpr deprecated +// ... 78 @atprotocolexpr deprecated +| 79 = @vastartexpr +| 80 = @vaargexpr +| 81 = @vaendexpr +| 82 = @vacopyexpr +// ... 83 @atencodeexpr deprecated +| 84 = @varaccess +| 85 = @thisaccess +// ... 86 @objc_box_expr deprecated +| 87 = @new_expr +| 88 = @delete_expr +| 89 = @throw_expr +| 90 = @condition_decl // a variable declared in a condition, e.g., if(int x = y > 2) +| 91 = @braced_init_list +| 92 = @type_id +| 93 = @runtime_sizeof +| 94 = @runtime_alignof +| 95 = @sizeof_pack +| 96 = @expr_stmt // GNU extension +| 97 = @routineexpr +| 98 = @type_operand // used to access a type in certain contexts (haven't found any examples yet....) +| 99 = @offsetofexpr // offsetof ::= type and field +| 100 = @hasassignexpr // __has_assign ::= type +| 101 = @hascopyexpr // __has_copy ::= type +| 102 = @hasnothrowassign // __has_nothrow_assign ::= type +| 103 = @hasnothrowconstr // __has_nothrow_constructor ::= type +| 104 = @hasnothrowcopy // __has_nothrow_copy ::= type +| 105 = @hastrivialassign // __has_trivial_assign ::= type +| 106 = @hastrivialconstr // __has_trivial_constructor ::= type +| 107 = @hastrivialcopy // __has_trivial_copy ::= type +| 108 = @hasuserdestr // __has_user_destructor ::= type +| 109 = @hasvirtualdestr // __has_virtual_destructor ::= type +| 110 = @isabstractexpr // __is_abstract ::= type +| 111 = @isbaseofexpr // __is_base_of ::= type type +| 112 = @isclassexpr // __is_class ::= type +| 113 = @isconvtoexpr // __is_convertible_to ::= type type +| 114 = @isemptyexpr // __is_empty ::= type +| 115 = @isenumexpr // __is_enum ::= type +| 116 = @ispodexpr // __is_pod ::= type +| 117 = @ispolyexpr // __is_polymorphic ::= type +| 118 = @isunionexpr // __is_union ::= type +| 119 = @typescompexpr // GNU __builtin_types_compatible ::= type type +| 120 = @intaddrexpr // EDG internal builtin, used to implement offsetof +// ... +| 122 = @hastrivialdestructor // __has_trivial_destructor ::= type +| 123 = @literal +| 124 = @uuidof +| 127 = @aggregateliteral +| 128 = @delete_array_expr +| 129 = @new_array_expr +// ... 130 @objc_array_literal deprecated +// ... 131 @objc_dictionary_literal deprecated +| 132 = @foldexpr +// ... +| 200 = @ctordirectinit +| 201 = @ctorvirtualinit +| 202 = @ctorfieldinit +| 203 = @ctordelegatinginit +| 204 = @dtordirectdestruct +| 205 = @dtorvirtualdestruct +| 206 = @dtorfielddestruct +// ... +| 210 = @static_cast +| 211 = @reinterpret_cast +| 212 = @const_cast +| 213 = @dynamic_cast +| 214 = @c_style_cast +| 215 = @lambdaexpr +| 216 = @param_ref +| 217 = @noopexpr +// ... +| 294 = @istriviallyconstructibleexpr +| 295 = @isdestructibleexpr +| 296 = @isnothrowdestructibleexpr +| 297 = @istriviallydestructibleexpr +| 298 = @istriviallyassignableexpr +| 299 = @isnothrowassignableexpr +| 300 = @istrivialexpr +| 301 = @isstandardlayoutexpr +| 302 = @istriviallycopyableexpr +| 303 = @isliteraltypeexpr +| 304 = @hastrivialmoveconstructorexpr +| 305 = @hastrivialmoveassignexpr +| 306 = @hasnothrowmoveassignexpr +| 307 = @isconstructibleexpr +| 308 = @isnothrowconstructibleexpr +| 309 = @hasfinalizerexpr +| 310 = @isdelegateexpr +| 311 = @isinterfaceclassexpr +| 312 = @isrefarrayexpr +| 313 = @isrefclassexpr +| 314 = @issealedexpr +| 315 = @issimplevalueclassexpr +| 316 = @isvalueclassexpr +| 317 = @isfinalexpr +| 319 = @noexceptexpr +| 320 = @builtinshufflevector +| 321 = @builtinchooseexpr +| 322 = @builtinaddressof +| 323 = @vec_fill +| 324 = @builtinconvertvector +| 325 = @builtincomplex +| 326 = @spaceshipexpr +| 327 = @co_await +| 328 = @co_yield +| 329 = @temp_init +| 330 = @isassignable +| 331 = @isaggregate +| 332 = @hasuniqueobjectrepresentations +| 333 = @builtinbitcast +| 334 = @builtinshuffle +; + +@var_args_expr = @vastartexpr + | @vaendexpr + | @vaargexpr + | @vacopyexpr + ; + +@builtin_op = @var_args_expr + | @noopexpr + | @offsetofexpr + | @intaddrexpr + | @hasassignexpr + | @hascopyexpr + | @hasnothrowassign + | @hasnothrowconstr + | @hasnothrowcopy + | @hastrivialassign + | @hastrivialconstr + | @hastrivialcopy + | @hastrivialdestructor + | @hasuserdestr + | @hasvirtualdestr + | @isabstractexpr + | @isbaseofexpr + | @isclassexpr + | @isconvtoexpr + | @isemptyexpr + | @isenumexpr + | @ispodexpr + | @ispolyexpr + | @isunionexpr + | @typescompexpr + | @builtinshufflevector + | @builtinconvertvector + | @builtinaddressof + | @istriviallyconstructibleexpr + | @isdestructibleexpr + | @isnothrowdestructibleexpr + | @istriviallydestructibleexpr + | @istriviallyassignableexpr + | @isnothrowassignableexpr + | @isstandardlayoutexpr + | @istriviallycopyableexpr + | @isliteraltypeexpr + | @hastrivialmoveconstructorexpr + | @hastrivialmoveassignexpr + | @hasnothrowmoveassignexpr + | @isconstructibleexpr + | @isnothrowconstructibleexpr + | @hasfinalizerexpr + | @isdelegateexpr + | @isinterfaceclassexpr + | @isrefarrayexpr + | @isrefclassexpr + | @issealedexpr + | @issimplevalueclassexpr + | @isvalueclassexpr + | @isfinalexpr + | @builtinchooseexpr + | @builtincomplex + | @isassignable + | @isaggregate + | @hasuniqueobjectrepresentations + | @builtinbitcast + | @builtinshuffle + ; + +new_allocated_type( + unique int expr: @new_expr ref, + int type_id: @type ref +); + +new_array_allocated_type( + unique int expr: @new_array_expr ref, + int type_id: @type ref +); + +/** + * The field being initialized by an initializer expression within an aggregate + * initializer for a class/struct/union. + */ +#keyset[aggregate, field] +aggregate_field_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int field: @membervariable ref +); + +/** + * The index of the element being initialized by an initializer expression + * within an aggregate initializer for an array. + */ +#keyset[aggregate, element_index] +aggregate_array_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int element_index: int ref +); + +@ctorinit = @ctordirectinit + | @ctorvirtualinit + | @ctorfieldinit + | @ctordelegatinginit; +@dtordestruct = @dtordirectdestruct + | @dtorvirtualdestruct + | @dtorfielddestruct; + + +condition_decl_bind( + unique int expr: @condition_decl ref, + unique int decl: @declaration ref +); + +typeid_bind( + unique int expr: @type_id ref, + int type_id: @type ref +); + +uuidof_bind( + unique int expr: @uuidof ref, + int type_id: @type ref +); + +@runtime_sizeof_or_alignof = @runtime_sizeof | @runtime_alignof; + +sizeof_bind( + unique int expr: @runtime_sizeof_or_alignof ref, + int type_id: @type ref +); + +code_block( + unique int block: @literal ref, + unique int routine: @function ref +); + +lambdas( + unique int expr: @lambdaexpr ref, + string default_capture: string ref, + boolean has_explicit_return_type: boolean ref +); + +lambda_capture( + unique int id: @lambdacapture, + int lambda: @lambdaexpr ref, + int index: int ref, + int field: @membervariable ref, + boolean captured_by_reference: boolean ref, + boolean is_implicit: boolean ref, + int location: @location_default ref +); + +@funbindexpr = @routineexpr + | @new_expr + | @delete_expr + | @delete_array_expr + | @ctordirectinit + | @ctorvirtualinit + | @ctordelegatinginit + | @dtordirectdestruct + | @dtorvirtualdestruct; + +@varbindexpr = @varaccess | @ctorfieldinit | @dtorfielddestruct; +@addressable = @function | @variable ; +@accessible = @addressable | @enumconstant ; + +@access = @varaccess | @routineexpr ; + +fold( + int expr: @foldexpr ref, + string operator: string ref, + boolean is_left_fold: boolean ref +); + +stmts( + unique int id: @stmt, + int kind: int ref, + int location: @location_stmt ref +); + +case @stmt.kind of + 1 = @stmt_expr +| 2 = @stmt_if +| 3 = @stmt_while +| 4 = @stmt_goto +| 5 = @stmt_label +| 6 = @stmt_return +| 7 = @stmt_block +| 8 = @stmt_end_test_while // do { ... } while ( ... ) +| 9 = @stmt_for +| 10 = @stmt_switch_case +| 11 = @stmt_switch +| 13 = @stmt_asm // "asm" statement or the body of an asm function +| 15 = @stmt_try_block +| 16 = @stmt_microsoft_try // Microsoft +| 17 = @stmt_decl +| 18 = @stmt_set_vla_size // C99 +| 19 = @stmt_vla_decl // C99 +| 25 = @stmt_assigned_goto // GNU +| 26 = @stmt_empty +| 27 = @stmt_continue +| 28 = @stmt_break +| 29 = @stmt_range_based_for // C++11 +// ... 30 @stmt_at_autoreleasepool_block deprecated +// ... 31 @stmt_objc_for_in deprecated +// ... 32 @stmt_at_synchronized deprecated +| 33 = @stmt_handler +// ... 34 @stmt_finally_end deprecated +| 35 = @stmt_constexpr_if +| 37 = @stmt_co_return +; + +type_vla( + int type_id: @type ref, + int decl: @stmt_vla_decl ref +); + +variable_vla( + int var: @variable ref, + int decl: @stmt_vla_decl ref +); + +if_initialization( + unique int if_stmt: @stmt_if ref, + int init_id: @stmt ref +); + +if_then( + unique int if_stmt: @stmt_if ref, + int then_id: @stmt ref +); + +if_else( + unique int if_stmt: @stmt_if ref, + int else_id: @stmt ref +); + +constexpr_if_initialization( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int init_id: @stmt ref +); + +constexpr_if_then( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int then_id: @stmt ref +); + +constexpr_if_else( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int else_id: @stmt ref +); + +while_body( + unique int while_stmt: @stmt_while ref, + int body_id: @stmt ref +); + +do_body( + unique int do_stmt: @stmt_end_test_while ref, + int body_id: @stmt ref +); + +switch_initialization( + unique int switch_stmt: @stmt_switch ref, + int init_id: @stmt ref +); + +#keyset[switch_stmt, index] +switch_case( + int switch_stmt: @stmt_switch ref, + int index: int ref, + int case_id: @stmt_switch_case ref +); + +switch_body( + unique int switch_stmt: @stmt_switch ref, + int body_id: @stmt ref +); + +for_initialization( + unique int for_stmt: @stmt_for ref, + int init_id: @stmt ref +); + +for_condition( + unique int for_stmt: @stmt_for ref, + int condition_id: @expr ref +); + +for_update( + unique int for_stmt: @stmt_for ref, + int update_id: @expr ref +); + +for_body( + unique int for_stmt: @stmt_for ref, + int body_id: @stmt ref +); + +@stmtparent = @stmt | @expr_stmt ; +stmtparents( + unique int id: @stmt ref, + int index: int ref, + int parent: @stmtparent ref +); + +ishandler(unique int block: @stmt_block ref); + +@cfgnode = @stmt | @expr | @function | @initialiser ; + +stmt_decl_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl: @declaration ref +); + +stmt_decl_entry_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl_entry: @element ref +); + +@functionorblock = @function | @stmt_block; + +blockscope( + unique int block: @stmt_block ref, + int enclosing: @functionorblock ref +); + +@jump = @stmt_goto | @stmt_break | @stmt_continue; + +@jumporlabel = @jump | @stmt_label | @literal; + +jumpinfo( + unique int id: @jumporlabel ref, + string str: string ref, + int target: @stmt ref +); + +preprocdirects( + unique int id: @preprocdirect, + int kind: int ref, + int location: @location_default ref +); +case @preprocdirect.kind of + 0 = @ppd_if +| 1 = @ppd_ifdef +| 2 = @ppd_ifndef +| 3 = @ppd_elif +| 4 = @ppd_else +| 5 = @ppd_endif +| 6 = @ppd_plain_include +| 7 = @ppd_define +| 8 = @ppd_undef +| 9 = @ppd_line +| 10 = @ppd_error +| 11 = @ppd_pragma +| 12 = @ppd_objc_import +| 13 = @ppd_include_next +| 18 = @ppd_warning +; + +@ppd_include = @ppd_plain_include | @ppd_objc_import | @ppd_include_next; + +@ppd_branch = @ppd_if | @ppd_ifdef | @ppd_ifndef | @ppd_elif; + +preprocpair( + int begin : @ppd_branch ref, + int elseelifend : @preprocdirect ref +); + +preproctrue(int branch : @ppd_branch ref); +preprocfalse(int branch : @ppd_branch ref); + +preproctext( + unique int id: @preprocdirect ref, + string head: string ref, + string body: string ref +); + +includes( + unique int id: @ppd_include ref, + int included: @file ref +); + +link_targets( + unique int id: @link_target, + int binary: @file ref +); + +link_parent( + int element : @element ref, + int link_target : @link_target ref +); + +/* XML Files */ + +xmlEncoding(unique int id: @file ref, string encoding: string ref); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters + | @xmlelement + | @xmlcomment + | @xmlattribute + | @xmldtd + | @file + | @xmlnamespace; diff --git a/cpp/downgrades/23f7cbb88a4eb29f30c3490363dc201bc054c5ff/semmlecode.cpp.dbscheme b/cpp/downgrades/23f7cbb88a4eb29f30c3490363dc201bc054c5ff/semmlecode.cpp.dbscheme new file mode 100644 index 00000000000..19e31bf071f --- /dev/null +++ b/cpp/downgrades/23f7cbb88a4eb29f30c3490363dc201bc054c5ff/semmlecode.cpp.dbscheme @@ -0,0 +1,2115 @@ + +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * gcc -c f1.c f2.c f3.c + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + /** + * An invocation of the compiler. Note that more than one file may + * be compiled per invocation. For example, this command compiles + * three source files: + * + * gcc -c f1.c f2.c f3.c + */ + unique int id : @compilation, + string cwd : string ref +); + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * gcc -c f1.c f2.c f3.c + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | *path to extractor* + * 1 | `--mimic` + * 2 | `/usr/bin/gcc` + * 3 | `-c` + * 4 | f1.c + * 5 | f2.c + * 6 | f3.c + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * gcc -c f1.c f2.c f3.c + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | f1.c + * 1 | f2.c + * 2 | f3.c + * + * Note that even if those files `#include` headers, those headers + * do not appear as rows. + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + + +/** + * External data, loaded from CSV files during snapshot creation. See + * [Tutorial: Incorporating external data](https://help.semmle.com/wiki/display/SD/Tutorial%3A+Incorporating+external+data) + * for more information. + */ +externalData( + int id : @externalDataElement, + string path : string ref, + int column: int ref, + string value : string ref +); + +/** + * The source location of the snapshot. + */ +sourceLocationPrefix(string prefix : string ref); + +/** + * Information about packages that provide code used during compilation. + * The `id` is just a unique identifier. + * The `namespace` is typically the name of the package manager that + * provided the package (e.g. "dpkg" or "yum"). + * The `package_name` is the name of the package, and `version` is its + * version (as a string). + */ +external_packages( + unique int id: @external_package, + string namespace : string ref, + string package_name : string ref, + string version : string ref +); + +/** + * Holds if File `fileid` was provided by package `package`. + */ +header_to_external_package( + int fileid : @file ref, + int package : @external_package ref +); + +/* + * Version history + */ + +svnentries( + unique int id : @svnentry, + string revision : string ref, + string author : string ref, + date revisionDate : date ref, + int changeSize : int ref +) + +svnaffectedfiles( + int id : @svnentry ref, + int file : @file ref, + string action : string ref +) + +svnentrymsg( + unique int id : @svnentry ref, + string message : string ref +) + +svnchurn( + int commit : @svnentry ref, + int file : @file ref, + int addedLines : int ref, + int deletedLines : int ref +) + +/* + * C++ dbscheme + */ + +@location = @location_stmt | @location_expr | @location_default ; + +/** + * The location of an element that is not an expression or a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ +locations_default( + /** The location of an element that is not an expression or a statement. */ + unique int id: @location_default, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ +locations_stmt( + /** The location of a statement. */ + unique int id: @location_stmt, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of an expression. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ +locations_expr( + /** The location of an expression. */ + unique int id: @location_expr, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** An element for which line-count information is available. */ +@sourceline = @file | @function | @variable | @enumconstant | @xmllocatable; + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +files( + unique int id: @file, + string name: string ref +); + +folders( + unique int id: @folder, + string name: string ref +); + +@container = @folder | @file + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +fileannotations( + int id: @file ref, + int kind: int ref, + string name: string ref, + string value: string ref +); + +inmacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +affectedbymacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +/* + case @macroinvocations.kind of + 1 = macro expansion + | 2 = other macro reference + ; +*/ +macroinvocations( + unique int id: @macroinvocation, + int macro_id: @ppd_define ref, + int location: @location_default ref, + int kind: int ref +); + +macroparent( + unique int id: @macroinvocation ref, + int parent_id: @macroinvocation ref +); + +// a macroinvocation may be part of another location +// the way to find a constant expression that uses a macro +// is thus to find a constant expression that has a location +// to which a macro invocation is bound +macrolocationbind( + int id: @macroinvocation ref, + int location: @location ref +); + +#keyset[invocation, argument_index] +macro_argument_unexpanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +#keyset[invocation, argument_index] +macro_argument_expanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +/* + case @function.kind of + 1 = normal + | 2 = constructor + | 3 = destructor + | 4 = conversion + | 5 = operator + | 6 = builtin // GCC built-in functions, e.g. __builtin___memcpy_chk + ; +*/ +functions( + unique int id: @function, + string name: string ref, + int kind: int ref +); + +function_entry_point(int id: @function ref, unique int entry_point: @stmt ref); + +function_return_type(int id: @function ref, int return_type: @type ref); + +/** If `function` is a coroutine, then this gives the + std::experimental::resumable_traits instance associated with it, + and the variables representing the `handle` and `promise` for it. */ +coroutine( + unique int function: @function ref, + int traits: @type ref, + int handle: @variable ref, + int promise: @variable ref +); + +/** The `new` function used for allocating the coroutine state, if any. */ +coroutine_new( + unique int function: @function ref, + int new: @function ref +); + +/** The `delete` function used for deallocating the coroutine state, if any. */ +coroutine_delete( + unique int function: @function ref, + int delete: @function ref +); + +purefunctions(unique int id: @function ref); + +function_deleted(unique int id: @function ref); + +function_defaulted(unique int id: @function ref); + +member_function_this_type(unique int id: @function ref, int this_type: @type ref); + +#keyset[id, type_id] +fun_decls( + int id: @fun_decl, + int function: @function ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +fun_def(unique int id: @fun_decl ref); +fun_specialized(unique int id: @fun_decl ref); +fun_implicit(unique int id: @fun_decl ref); +fun_decl_specifiers( + int id: @fun_decl ref, + string name: string ref +) +#keyset[fun_decl, index] +fun_decl_throws( + int fun_decl: @fun_decl ref, + int index: int ref, + int type_id: @type ref +); +/* an empty throw specification is different from none */ +fun_decl_empty_throws(unique int fun_decl: @fun_decl ref); +fun_decl_noexcept( + int fun_decl: @fun_decl ref, + int constant: @expr ref +); +fun_decl_empty_noexcept(int fun_decl: @fun_decl ref); +fun_decl_typedef_type( + unique int fun_decl: @fun_decl ref, + int typedeftype_id: @usertype ref +); + +param_decl_bind( + unique int id: @var_decl ref, + int index: int ref, + int fun_decl: @fun_decl ref +); + +#keyset[id, type_id] +var_decls( + int id: @var_decl, + int variable: @variable ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +var_def(unique int id: @var_decl ref); +var_decl_specifiers( + int id: @var_decl ref, + string name: string ref +) +is_structured_binding(unique int id: @variable ref); + +type_decls( + unique int id: @type_decl, + int type_id: @type ref, + int location: @location_default ref +); +type_def(unique int id: @type_decl ref); +type_decl_top( + unique int type_decl: @type_decl ref +); + +namespace_decls( + unique int id: @namespace_decl, + int namespace_id: @namespace ref, + int location: @location_default ref, + int bodylocation: @location_default ref +); + +usings( + unique int id: @using, + int element_id: @element ref, + int location: @location_default ref +); + +/** The element which contains the `using` declaration. */ +using_container( + int parent: @element ref, + int child: @using ref +); + +static_asserts( + unique int id: @static_assert, + int condition : @expr ref, + string message : string ref, + int location: @location_default ref, + int enclosing : @element ref +); + +// each function has an ordered list of parameters +#keyset[id, type_id] +#keyset[function, index, type_id] +params( + int id: @parameter, + int function: @functionorblock ref, + int index: int ref, + int type_id: @type ref +); + +overrides(int new: @function ref, int old: @function ref); + +#keyset[id, type_id] +membervariables( + int id: @membervariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +globalvariables( + int id: @globalvariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +localvariables( + int id: @localvariable, + int type_id: @type ref, + string name: string ref +); + +autoderivation( + unique int var: @variable ref, + int derivation_type: @type ref +); + +enumconstants( + unique int id: @enumconstant, + int parent: @usertype ref, + int index: int ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); + +@variable = @localscopevariable | @globalvariable | @membervariable; + +@localscopevariable = @localvariable | @parameter; + +/* + Built-in types are the fundamental types, e.g., integral, floating, and void. + + case @builtintype.kind of + 1 = error + | 2 = unknown + | 3 = void + | 4 = boolean + | 5 = char + | 6 = unsigned_char + | 7 = signed_char + | 8 = short + | 9 = unsigned_short + | 10 = signed_short + | 11 = int + | 12 = unsigned_int + | 13 = signed_int + | 14 = long + | 15 = unsigned_long + | 16 = signed_long + | 17 = long_long + | 18 = unsigned_long_long + | 19 = signed_long_long + | 20 = __int8 // Microsoft-specific + | 21 = __int16 // Microsoft-specific + | 22 = __int32 // Microsoft-specific + | 23 = __int64 // Microsoft-specific + | 24 = float + | 25 = double + | 26 = long_double + | 27 = _Complex_float // C99-specific + | 28 = _Complex_double // C99-specific + | 29 = _Complex_long double // C99-specific + | 30 = _Imaginary_float // C99-specific + | 31 = _Imaginary_double // C99-specific + | 32 = _Imaginary_long_double // C99-specific + | 33 = wchar_t // Microsoft-specific + | 34 = decltype_nullptr // C++11 + | 35 = __int128 + | 36 = unsigned___int128 + | 37 = signed___int128 + | 38 = __float128 + | 39 = _Complex___float128 + | 40 = _Decimal32 + | 41 = _Decimal64 + | 42 = _Decimal128 + | 43 = char16_t + | 44 = char32_t + | 45 = _Float32 + | 46 = _Float32x + | 47 = _Float64 + | 48 = _Float64x + | 49 = _Float128 + | 50 = _Float128x + | 51 = char8_t + ; +*/ +builtintypes( + unique int id: @builtintype, + string name: string ref, + int kind: int ref, + int size: int ref, + int sign: int ref, + int alignment: int ref +); + +/* + Derived types are types that are directly derived from existing types and + point to, refer to, transform type data to return a new type. + + case @derivedtype.kind of + 1 = pointer + | 2 = reference + | 3 = type_with_specifiers + | 4 = array + | 5 = gnu_vector + | 6 = routineptr + | 7 = routinereference + | 8 = rvalue_reference // C++11 +// ... 9 type_conforming_to_protocols deprecated + | 10 = block + ; +*/ +derivedtypes( + unique int id: @derivedtype, + string name: string ref, + int kind: int ref, + int type_id: @type ref +); + +pointerishsize(unique int id: @derivedtype ref, + int size: int ref, + int alignment: int ref); + +arraysizes( + unique int id: @derivedtype ref, + int num_elements: int ref, + int bytesize: int ref, + int alignment: int ref +); + +typedefbase( + unique int id: @usertype ref, + int type_id: @type ref +); + +/** + * An instance of the C++11 `decltype` operator. For example: + * ``` + * int a; + * decltype(1+a) b; + * ``` + * Here `expr` is `1+a`. + * + * Sometimes an additional pair of parentheses around the expression + * would change the semantics of this decltype, e.g. + * ``` + * struct A { double x; }; + * const A* a = new A(); + * decltype( a->x ); // type is double + * decltype((a->x)); // type is const double& + * ``` + * (Please consult the C++11 standard for more details). + * `parentheses_would_change_meaning` is `true` iff that is the case. + */ +#keyset[id, expr] +decltypes( + int id: @decltype, + int expr: @expr ref, + int base_type: @type ref, + boolean parentheses_would_change_meaning: boolean ref +); + +/* + case @usertype.kind of + 1 = struct + | 2 = class + | 3 = union + | 4 = enum + | 5 = typedef // classic C: typedef typedef type name + | 6 = template + | 7 = template_parameter + | 8 = template_template_parameter + | 9 = proxy_class // a proxy class associated with a template parameter +// ... 10 objc_class deprecated +// ... 11 objc_protocol deprecated +// ... 12 objc_category deprecated + | 13 = scoped_enum + | 14 = using_alias // a using name = type style typedef + ; +*/ +usertypes( + unique int id: @usertype, + string name: string ref, + int kind: int ref +); + +usertypesize( + unique int id: @usertype ref, + int size: int ref, + int alignment: int ref +); + +usertype_final(unique int id: @usertype ref); + +usertype_uuid( + unique int id: @usertype ref, + string uuid: string ref +); + +mangled_name( + unique int id: @declaration ref, + int mangled_name : @mangledname +); + +is_pod_class(unique int id: @usertype ref); +is_standard_layout_class(unique int id: @usertype ref); + +is_complete(unique int id: @usertype ref); + +is_class_template(unique int id: @usertype ref); +class_instantiation( + int to: @usertype ref, + int from: @usertype ref +); +class_template_argument( + int type_id: @usertype ref, + int index: int ref, + int arg_type: @type ref +); +class_template_argument_value( + int type_id: @usertype ref, + int index: int ref, + int arg_value: @expr ref +); + +is_proxy_class_for( + unique int id: @usertype ref, + unique int templ_param_id: @usertype ref +); + +type_mentions( + unique int id: @type_mention, + int type_id: @type ref, + int location: @location ref, + // a_symbol_reference_kind from the EDG frontend. See symbol_ref.h there. + int kind: int ref +); + +is_function_template(unique int id: @function ref); +function_instantiation( + unique int to: @function ref, + int from: @function ref +); +function_template_argument( + int function_id: @function ref, + int index: int ref, + int arg_type: @type ref +); +function_template_argument_value( + int function_id: @function ref, + int index: int ref, + int arg_value: @expr ref +); + +is_variable_template(unique int id: @variable ref); +variable_instantiation( + unique int to: @variable ref, + int from: @variable ref +); +variable_template_argument( + int variable_id: @variable ref, + int index: int ref, + int arg_type: @type ref +); +variable_template_argument_value( + int variable_id: @variable ref, + int index: int ref, + int arg_value: @expr ref +); + +/* + Fixed point types + precision(1) = short, precision(2) = default, precision(3) = long + is_unsigned(1) = unsigned is_unsigned(2) = signed + is_fract_type(1) = declared with _Fract + saturating(1) = declared with _Sat +*/ +/* TODO +fixedpointtypes( + unique int id: @fixedpointtype, + int precision: int ref, + int is_unsigned: int ref, + int is_fract_type: int ref, + int saturating: int ref); +*/ + +routinetypes( + unique int id: @routinetype, + int return_type: @type ref +); + +routinetypeargs( + int routine: @routinetype ref, + int index: int ref, + int type_id: @type ref +); + +ptrtomembers( + unique int id: @ptrtomember, + int type_id: @type ref, + int class_id: @type ref +); + +/* + specifiers for types, functions, and variables + + "public", + "protected", + "private", + + "const", + "volatile", + "static", + + "pure", + "virtual", + "sealed", // Microsoft + "__interface", // Microsoft + "inline", + "explicit", + + "near", // near far extension + "far", // near far extension + "__ptr32", // Microsoft + "__ptr64", // Microsoft + "__sptr", // Microsoft + "__uptr", // Microsoft + "dllimport", // Microsoft + "dllexport", // Microsoft + "thread", // Microsoft + "naked", // Microsoft + "microsoft_inline", // Microsoft + "forceinline", // Microsoft + "selectany", // Microsoft + "nothrow", // Microsoft + "novtable", // Microsoft + "noreturn", // Microsoft + "noinline", // Microsoft + "noalias", // Microsoft + "restrict", // Microsoft +*/ + +specifiers( + unique int id: @specifier, + unique string str: string ref +); + +typespecifiers( + int type_id: @type ref, + int spec_id: @specifier ref +); + +funspecifiers( + int func_id: @function ref, + int spec_id: @specifier ref +); + +varspecifiers( + int var_id: @accessible ref, + int spec_id: @specifier ref +); + +attributes( + unique int id: @attribute, + int kind: int ref, + string name: string ref, + string name_space: string ref, + int location: @location_default ref +); + +case @attribute.kind of + 0 = @gnuattribute +| 1 = @stdattribute +| 2 = @declspec +| 3 = @msattribute +| 4 = @alignas +// ... 5 @objc_propertyattribute deprecated +; + +attribute_args( + unique int id: @attribute_arg, + int kind: int ref, + int attribute: @attribute ref, + int index: int ref, + int location: @location_default ref +); + +case @attribute_arg.kind of + 0 = @attribute_arg_empty +| 1 = @attribute_arg_token +| 2 = @attribute_arg_constant +| 3 = @attribute_arg_type +; + +attribute_arg_value( + unique int arg: @attribute_arg ref, + string value: string ref +); +attribute_arg_type( + unique int arg: @attribute_arg ref, + int type_id: @type ref +); +attribute_arg_name( + unique int arg: @attribute_arg ref, + string name: string ref +); + +typeattributes( + int type_id: @type ref, + int spec_id: @attribute ref +); + +funcattributes( + int func_id: @function ref, + int spec_id: @attribute ref +); + +varattributes( + int var_id: @accessible ref, + int spec_id: @attribute ref +); + +stmtattributes( + int stmt_id: @stmt ref, + int spec_id: @attribute ref +); + +@type = @builtintype + | @derivedtype + | @usertype + /* TODO | @fixedpointtype */ + | @routinetype + | @ptrtomember + | @decltype; + +unspecifiedtype( + unique int type_id: @type ref, + int unspecified_type_id: @type ref +); + +member( + int parent: @type ref, + int index: int ref, + int child: @member ref +); + +@enclosingfunction_child = @usertype | @variable | @namespace + +enclosingfunction( + unique int child: @enclosingfunction_child ref, + int parent: @function ref +); + +derivations( + unique int derivation: @derivation, + int sub: @type ref, + int index: int ref, + int super: @type ref, + int location: @location_default ref +); + +derspecifiers( + int der_id: @derivation ref, + int spec_id: @specifier ref +); + +/** + * Contains the byte offset of the base class subobject within the derived + * class. Only holds for non-virtual base classes, but see table + * `virtual_base_offsets` for offsets of virtual base class subobjects. + */ +direct_base_offsets( + unique int der_id: @derivation ref, + int offset: int ref +); + +/** + * Contains the byte offset of the virtual base class subobject for class + * `super` within a most-derived object of class `sub`. `super` can be either a + * direct or indirect base class. + */ +#keyset[sub, super] +virtual_base_offsets( + int sub: @usertype ref, + int super: @usertype ref, + int offset: int ref +); + +frienddecls( + unique int id: @frienddecl, + int type_id: @type ref, + int decl_id: @declaration ref, + int location: @location_default ref +); + +@declaredtype = @usertype ; + +@declaration = @function + | @declaredtype + | @variable + | @enumconstant + | @frienddecl; + +@member = @membervariable + | @function + | @declaredtype + | @enumconstant; + +@locatable = @diagnostic + | @declaration + | @ppd_include + | @ppd_define + | @macroinvocation + /*| @funcall*/ + | @xmllocatable + | @attribute + | @attribute_arg; + +@namedscope = @namespace | @usertype; + +@element = @locatable + | @file + | @folder + | @specifier + | @type + | @expr + | @namespace + | @initialiser + | @stmt + | @derivation + | @comment + | @preprocdirect + | @fun_decl + | @var_decl + | @type_decl + | @namespace_decl + | @using + | @namequalifier + | @specialnamequalifyingelement + | @static_assert + | @type_mention + | @lambdacapture; + +@exprparent = @element; + +comments( + unique int id: @comment, + string contents: string ref, + int location: @location_default ref +); + +commentbinding( + int id: @comment ref, + int element: @element ref +); + +exprconv( + int converted: @expr ref, + unique int conversion: @expr ref +); + +compgenerated(unique int id: @element ref); + +/** + * `destructor_call` destructs the `i`'th entity that should be + * destructed following `element`. Note that entities should be + * destructed in reverse construction order, so for a given `element` + * these should be called from highest to lowest `i`. + */ +#keyset[element, destructor_call] +#keyset[element, i] +synthetic_destructor_call( + int element: @element ref, + int i: int ref, + int destructor_call: @routineexpr ref +); + +namespaces( + unique int id: @namespace, + string name: string ref +); + +namespace_inline( + unique int id: @namespace ref +); + +namespacembrs( + int parentid: @namespace ref, + unique int memberid: @namespacembr ref +); + +@namespacembr = @declaration | @namespace; + +exprparents( + int expr_id: @expr ref, + int child_index: int ref, + int parent_id: @exprparent ref +); + +expr_isload(unique int expr_id: @expr ref); + +@cast = @c_style_cast + | @const_cast + | @dynamic_cast + | @reinterpret_cast + | @static_cast + ; + +/* +case @conversion.kind of + 0 = @simple_conversion // a numeric conversion, qualification conversion, or a reinterpret_cast +| 1 = @bool_conversion // conversion to 'bool' +| 2 = @base_class_conversion // a derived-to-base conversion +| 3 = @derived_class_conversion // a base-to-derived conversion +| 4 = @pm_base_class_conversion // a derived-to-base conversion of a pointer to member +| 5 = @pm_derived_class_conversion // a base-to-derived conversion of a pointer to member +| 6 = @glvalue_adjust // an adjustment of the type of a glvalue +| 7 = @prvalue_adjust // an adjustment of the type of a prvalue +; +*/ +/** + * Describes the semantics represented by a cast expression. This is largely + * independent of the source syntax of the cast, so it is separate from the + * regular expression kind. + */ +conversionkinds( + unique int expr_id: @cast ref, + int kind: int ref +); + +@conversion = @cast + | @array_to_pointer + | @parexpr + | @reference_to + | @ref_indirect + | @temp_init + ; + +/* +case @funbindexpr.kind of + 0 = @normal_call // a normal call +| 1 = @virtual_call // a virtual call +| 2 = @adl_call // a call whose target is only found by ADL +; +*/ +iscall(unique int caller: @funbindexpr ref, int kind: int ref); + +numtemplatearguments( + unique int expr_id: @expr ref, + int num: int ref +); + +specialnamequalifyingelements( + unique int id: @specialnamequalifyingelement, + unique string name: string ref +); + +@namequalifiableelement = @expr | @namequalifier; +@namequalifyingelement = @namespace + | @specialnamequalifyingelement + | @usertype; + +namequalifiers( + unique int id: @namequalifier, + unique int qualifiableelement: @namequalifiableelement ref, + int qualifyingelement: @namequalifyingelement ref, + int location: @location_default ref +); + +varbind( + int expr: @varbindexpr ref, + int var: @accessible ref +); + +funbind( + int expr: @funbindexpr ref, + int fun: @function ref +); + +@any_new_expr = @new_expr + | @new_array_expr; + +@new_or_delete_expr = @any_new_expr + | @delete_expr + | @delete_array_expr; + +@prefix_crement_expr = @preincrexpr | @predecrexpr; + +@postfix_crement_expr = @postincrexpr | @postdecrexpr; + +@increment_expr = @preincrexpr | @postincrexpr; + +@decrement_expr = @predecrexpr | @postdecrexpr; + +@crement_expr = @increment_expr | @decrement_expr; + +@un_arith_op_expr = @arithnegexpr + | @unaryplusexpr + | @conjugation + | @realpartexpr + | @imagpartexpr + | @crement_expr + ; + +@un_bitwise_op_expr = @complementexpr; + +@un_log_op_expr = @notexpr; + +@un_op_expr = @address_of + | @indirect + | @un_arith_op_expr + | @un_bitwise_op_expr + | @builtinaddressof + | @vec_fill + | @un_log_op_expr + | @co_await + | @co_yield + ; + +@bin_log_op_expr = @andlogicalexpr | @orlogicalexpr; + +@cmp_op_expr = @eq_op_expr | @rel_op_expr; + +@eq_op_expr = @eqexpr | @neexpr; + +@rel_op_expr = @gtexpr + | @ltexpr + | @geexpr + | @leexpr + | @spaceshipexpr + ; + +@bin_bitwise_op_expr = @lshiftexpr + | @rshiftexpr + | @andexpr + | @orexpr + | @xorexpr + ; + +@p_arith_op_expr = @paddexpr + | @psubexpr + | @pdiffexpr + ; + +@bin_arith_op_expr = @addexpr + | @subexpr + | @mulexpr + | @divexpr + | @remexpr + | @jmulexpr + | @jdivexpr + | @fjaddexpr + | @jfaddexpr + | @fjsubexpr + | @jfsubexpr + | @minexpr + | @maxexpr + | @p_arith_op_expr + ; + +@bin_op_expr = @bin_arith_op_expr + | @bin_bitwise_op_expr + | @cmp_op_expr + | @bin_log_op_expr + ; + +@op_expr = @un_op_expr + | @bin_op_expr + | @assign_expr + | @conditionalexpr + ; + +@assign_arith_expr = @assignaddexpr + | @assignsubexpr + | @assignmulexpr + | @assigndivexpr + | @assignremexpr + ; + +@assign_bitwise_expr = @assignandexpr + | @assignorexpr + | @assignxorexpr + | @assignlshiftexpr + | @assignrshiftexpr + | @assignpaddexpr + | @assignpsubexpr + ; + +@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr + +@assign_expr = @assignexpr | @assign_op_expr + +/* + case @allocator.form of + 0 = plain + | 1 = alignment + ; +*/ + +/** + * The allocator function associated with a `new` or `new[]` expression. + * The `form` column specified whether the allocation call contains an alignment + * argument. + */ +expr_allocator( + unique int expr: @any_new_expr ref, + int func: @function ref, + int form: int ref +); + +/* + case @deallocator.form of + 0 = plain + | 1 = size + | 2 = alignment + | 3 = size_and_alignment + ; +*/ + +/** + * The deallocator function associated with a `delete`, `delete[]`, `new`, or + * `new[]` expression. For a `new` or `new[]` expression, the deallocator is the + * one used to free memory if the initialization throws an exception. + * The `form` column specifies whether the deallocation call contains a size + * argument, and alignment argument, or both. + */ +expr_deallocator( + unique int expr: @new_or_delete_expr ref, + int func: @function ref, + int form: int ref +); + +/** + * Holds if the `@conditionalexpr` is of the two operand form + * `guard ? : false`. + */ +expr_cond_two_operand( + unique int cond: @conditionalexpr ref +); + +/** + * The guard of `@conditionalexpr` `guard ? true : false` + */ +expr_cond_guard( + unique int cond: @conditionalexpr ref, + int guard: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` holds. For the two operand form + * `guard ?: false` consider using `expr_cond_guard` instead. + */ +expr_cond_true( + unique int cond: @conditionalexpr ref, + int true: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` does not hold. + */ +expr_cond_false( + unique int cond: @conditionalexpr ref, + int false: @expr ref +); + +/** A string representation of the value. */ +values( + unique int id: @value, + string str: string ref +); + +/** The actual text in the source code for the value, if any. */ +valuetext( + unique int id: @value ref, + string text: string ref +); + +valuebind( + int val: @value ref, + unique int expr: @expr ref +); + +fieldoffsets( + unique int id: @variable ref, + int byteoffset: int ref, + int bitoffset: int ref +); + +bitfield( + unique int id: @variable ref, + int bits: int ref, + int declared_bits: int ref +); + +/* TODO +memberprefix( + int member: @expr ref, + int prefix: @expr ref +); +*/ + +/* + kind(1) = mbrcallexpr + kind(2) = mbrptrcallexpr + kind(3) = mbrptrmbrcallexpr + kind(4) = ptrmbrptrmbrcallexpr + kind(5) = mbrreadexpr // x.y + kind(6) = mbrptrreadexpr // p->y + kind(7) = mbrptrmbrreadexpr // x.*pm + kind(8) = mbrptrmbrptrreadexpr // x->*pm + kind(9) = staticmbrreadexpr // static x.y + kind(10) = staticmbrptrreadexpr // static p->y +*/ +/* TODO +memberaccess( + int member: @expr ref, + int kind: int ref +); +*/ + +initialisers( + unique int init: @initialiser, + int var: @accessible ref, + unique int expr: @expr ref, + int location: @location_expr ref +); + +braced_initialisers( + int init: @initialiser ref +); + +/** + * An ancestor for the expression, for cases in which we cannot + * otherwise find the expression's parent. + */ +expr_ancestor( + int exp: @expr ref, + int ancestor: @element ref +); + +exprs( + unique int id: @expr, + int kind: int ref, + int location: @location_expr ref +); + +/* + case @value.category of + 1 = prval + | 2 = xval + | 3 = lval + ; +*/ +expr_types( + int id: @expr ref, + int typeid: @type ref, + int value_category: int ref +); + +case @expr.kind of + 1 = @errorexpr +| 2 = @address_of // & AddressOfExpr +| 3 = @reference_to // ReferenceToExpr (implicit?) +| 4 = @indirect // * PointerDereferenceExpr +| 5 = @ref_indirect // ReferenceDereferenceExpr (implicit?) +// ... +| 8 = @array_to_pointer // (???) +| 9 = @vacuous_destructor_call // VacuousDestructorCall +// ... +| 11 = @assume // Microsoft +| 12 = @parexpr +| 13 = @arithnegexpr +| 14 = @unaryplusexpr +| 15 = @complementexpr +| 16 = @notexpr +| 17 = @conjugation // GNU ~ operator +| 18 = @realpartexpr // GNU __real +| 19 = @imagpartexpr // GNU __imag +| 20 = @postincrexpr +| 21 = @postdecrexpr +| 22 = @preincrexpr +| 23 = @predecrexpr +| 24 = @conditionalexpr +| 25 = @addexpr +| 26 = @subexpr +| 27 = @mulexpr +| 28 = @divexpr +| 29 = @remexpr +| 30 = @jmulexpr // C99 mul imaginary +| 31 = @jdivexpr // C99 div imaginary +| 32 = @fjaddexpr // C99 add real + imaginary +| 33 = @jfaddexpr // C99 add imaginary + real +| 34 = @fjsubexpr // C99 sub real - imaginary +| 35 = @jfsubexpr // C99 sub imaginary - real +| 36 = @paddexpr // pointer add (pointer + int or int + pointer) +| 37 = @psubexpr // pointer sub (pointer - integer) +| 38 = @pdiffexpr // difference between two pointers +| 39 = @lshiftexpr +| 40 = @rshiftexpr +| 41 = @andexpr +| 42 = @orexpr +| 43 = @xorexpr +| 44 = @eqexpr +| 45 = @neexpr +| 46 = @gtexpr +| 47 = @ltexpr +| 48 = @geexpr +| 49 = @leexpr +| 50 = @minexpr // GNU minimum +| 51 = @maxexpr // GNU maximum +| 52 = @assignexpr +| 53 = @assignaddexpr +| 54 = @assignsubexpr +| 55 = @assignmulexpr +| 56 = @assigndivexpr +| 57 = @assignremexpr +| 58 = @assignlshiftexpr +| 59 = @assignrshiftexpr +| 60 = @assignandexpr +| 61 = @assignorexpr +| 62 = @assignxorexpr +| 63 = @assignpaddexpr // assign pointer add +| 64 = @assignpsubexpr // assign pointer sub +| 65 = @andlogicalexpr +| 66 = @orlogicalexpr +| 67 = @commaexpr +| 68 = @subscriptexpr // access to member of an array, e.g., a[5] +// ... 69 @objc_subscriptexpr deprecated +// ... 70 @cmdaccess deprecated +// ... +| 73 = @virtfunptrexpr +| 74 = @callexpr +// ... 75 @msgexpr_normal deprecated +// ... 76 @msgexpr_super deprecated +// ... 77 @atselectorexpr deprecated +// ... 78 @atprotocolexpr deprecated +| 79 = @vastartexpr +| 80 = @vaargexpr +| 81 = @vaendexpr +| 82 = @vacopyexpr +// ... 83 @atencodeexpr deprecated +| 84 = @varaccess +| 85 = @thisaccess +// ... 86 @objc_box_expr deprecated +| 87 = @new_expr +| 88 = @delete_expr +| 89 = @throw_expr +| 90 = @condition_decl // a variable declared in a condition, e.g., if(int x = y > 2) +| 91 = @braced_init_list +| 92 = @type_id +| 93 = @runtime_sizeof +| 94 = @runtime_alignof +| 95 = @sizeof_pack +| 96 = @expr_stmt // GNU extension +| 97 = @routineexpr +| 98 = @type_operand // used to access a type in certain contexts (haven't found any examples yet....) +| 99 = @offsetofexpr // offsetof ::= type and field +| 100 = @hasassignexpr // __has_assign ::= type +| 101 = @hascopyexpr // __has_copy ::= type +| 102 = @hasnothrowassign // __has_nothrow_assign ::= type +| 103 = @hasnothrowconstr // __has_nothrow_constructor ::= type +| 104 = @hasnothrowcopy // __has_nothrow_copy ::= type +| 105 = @hastrivialassign // __has_trivial_assign ::= type +| 106 = @hastrivialconstr // __has_trivial_constructor ::= type +| 107 = @hastrivialcopy // __has_trivial_copy ::= type +| 108 = @hasuserdestr // __has_user_destructor ::= type +| 109 = @hasvirtualdestr // __has_virtual_destructor ::= type +| 110 = @isabstractexpr // __is_abstract ::= type +| 111 = @isbaseofexpr // __is_base_of ::= type type +| 112 = @isclassexpr // __is_class ::= type +| 113 = @isconvtoexpr // __is_convertible_to ::= type type +| 114 = @isemptyexpr // __is_empty ::= type +| 115 = @isenumexpr // __is_enum ::= type +| 116 = @ispodexpr // __is_pod ::= type +| 117 = @ispolyexpr // __is_polymorphic ::= type +| 118 = @isunionexpr // __is_union ::= type +| 119 = @typescompexpr // GNU __builtin_types_compatible ::= type type +| 120 = @intaddrexpr // EDG internal builtin, used to implement offsetof +// ... +| 122 = @hastrivialdestructor // __has_trivial_destructor ::= type +| 123 = @literal +| 124 = @uuidof +| 127 = @aggregateliteral +| 128 = @delete_array_expr +| 129 = @new_array_expr +// ... 130 @objc_array_literal deprecated +// ... 131 @objc_dictionary_literal deprecated +| 132 = @foldexpr +// ... +| 200 = @ctordirectinit +| 201 = @ctorvirtualinit +| 202 = @ctorfieldinit +| 203 = @ctordelegatinginit +| 204 = @dtordirectdestruct +| 205 = @dtorvirtualdestruct +| 206 = @dtorfielddestruct +// ... +| 210 = @static_cast +| 211 = @reinterpret_cast +| 212 = @const_cast +| 213 = @dynamic_cast +| 214 = @c_style_cast +| 215 = @lambdaexpr +| 216 = @param_ref +| 217 = @noopexpr +// ... +| 294 = @istriviallyconstructibleexpr +| 295 = @isdestructibleexpr +| 296 = @isnothrowdestructibleexpr +| 297 = @istriviallydestructibleexpr +| 298 = @istriviallyassignableexpr +| 299 = @isnothrowassignableexpr +| 300 = @istrivialexpr +| 301 = @isstandardlayoutexpr +| 302 = @istriviallycopyableexpr +| 303 = @isliteraltypeexpr +| 304 = @hastrivialmoveconstructorexpr +| 305 = @hastrivialmoveassignexpr +| 306 = @hasnothrowmoveassignexpr +| 307 = @isconstructibleexpr +| 308 = @isnothrowconstructibleexpr +| 309 = @hasfinalizerexpr +| 310 = @isdelegateexpr +| 311 = @isinterfaceclassexpr +| 312 = @isrefarrayexpr +| 313 = @isrefclassexpr +| 314 = @issealedexpr +| 315 = @issimplevalueclassexpr +| 316 = @isvalueclassexpr +| 317 = @isfinalexpr +| 319 = @noexceptexpr +| 320 = @builtinshufflevector +| 321 = @builtinchooseexpr +| 322 = @builtinaddressof +| 323 = @vec_fill +| 324 = @builtinconvertvector +| 325 = @builtincomplex +| 326 = @spaceshipexpr +| 327 = @co_await +| 328 = @co_yield +| 329 = @temp_init +; + +@var_args_expr = @vastartexpr + | @vaendexpr + | @vaargexpr + | @vacopyexpr + ; + +@builtin_op = @var_args_expr + | @noopexpr + | @offsetofexpr + | @intaddrexpr + | @hasassignexpr + | @hascopyexpr + | @hasnothrowassign + | @hasnothrowconstr + | @hasnothrowcopy + | @hastrivialassign + | @hastrivialconstr + | @hastrivialcopy + | @hastrivialdestructor + | @hasuserdestr + | @hasvirtualdestr + | @isabstractexpr + | @isbaseofexpr + | @isclassexpr + | @isconvtoexpr + | @isemptyexpr + | @isenumexpr + | @ispodexpr + | @ispolyexpr + | @isunionexpr + | @typescompexpr + | @builtinshufflevector + | @builtinconvertvector + | @builtinaddressof + | @istriviallyconstructibleexpr + | @isdestructibleexpr + | @isnothrowdestructibleexpr + | @istriviallydestructibleexpr + | @istriviallyassignableexpr + | @isnothrowassignableexpr + | @isstandardlayoutexpr + | @istriviallycopyableexpr + | @isliteraltypeexpr + | @hastrivialmoveconstructorexpr + | @hastrivialmoveassignexpr + | @hasnothrowmoveassignexpr + | @isconstructibleexpr + | @isnothrowconstructibleexpr + | @hasfinalizerexpr + | @isdelegateexpr + | @isinterfaceclassexpr + | @isrefarrayexpr + | @isrefclassexpr + | @issealedexpr + | @issimplevalueclassexpr + | @isvalueclassexpr + | @isfinalexpr + | @builtinchooseexpr + | @builtincomplex + ; + +new_allocated_type( + unique int expr: @new_expr ref, + int type_id: @type ref +); + +new_array_allocated_type( + unique int expr: @new_array_expr ref, + int type_id: @type ref +); + +/** + * The field being initialized by an initializer expression within an aggregate + * initializer for a class/struct/union. + */ +#keyset[aggregate, field] +aggregate_field_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int field: @membervariable ref +); + +/** + * The index of the element being initialized by an initializer expression + * within an aggregate initializer for an array. + */ +#keyset[aggregate, element_index] +aggregate_array_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int element_index: int ref +); + +@ctorinit = @ctordirectinit + | @ctorvirtualinit + | @ctorfieldinit + | @ctordelegatinginit; +@dtordestruct = @dtordirectdestruct + | @dtorvirtualdestruct + | @dtorfielddestruct; + + +condition_decl_bind( + unique int expr: @condition_decl ref, + unique int decl: @declaration ref +); + +typeid_bind( + unique int expr: @type_id ref, + int type_id: @type ref +); + +uuidof_bind( + unique int expr: @uuidof ref, + int type_id: @type ref +); + +@runtime_sizeof_or_alignof = @runtime_sizeof | @runtime_alignof; + +sizeof_bind( + unique int expr: @runtime_sizeof_or_alignof ref, + int type_id: @type ref +); + +code_block( + unique int block: @literal ref, + unique int routine: @function ref +); + +lambdas( + unique int expr: @lambdaexpr ref, + string default_capture: string ref, + boolean has_explicit_return_type: boolean ref +); + +lambda_capture( + unique int id: @lambdacapture, + int lambda: @lambdaexpr ref, + int index: int ref, + int field: @membervariable ref, + boolean captured_by_reference: boolean ref, + boolean is_implicit: boolean ref, + int location: @location_default ref +); + +@funbindexpr = @routineexpr + | @new_expr + | @delete_expr + | @delete_array_expr + | @ctordirectinit + | @ctorvirtualinit + | @ctordelegatinginit + | @dtordirectdestruct + | @dtorvirtualdestruct; + +@varbindexpr = @varaccess | @ctorfieldinit | @dtorfielddestruct; +@addressable = @function | @variable ; +@accessible = @addressable | @enumconstant ; + +@access = @varaccess | @routineexpr ; + +fold( + int expr: @foldexpr ref, + string operator: string ref, + boolean is_left_fold: boolean ref +); + +stmts( + unique int id: @stmt, + int kind: int ref, + int location: @location_stmt ref +); + +case @stmt.kind of + 1 = @stmt_expr +| 2 = @stmt_if +| 3 = @stmt_while +| 4 = @stmt_goto +| 5 = @stmt_label +| 6 = @stmt_return +| 7 = @stmt_block +| 8 = @stmt_end_test_while // do { ... } while ( ... ) +| 9 = @stmt_for +| 10 = @stmt_switch_case +| 11 = @stmt_switch +| 13 = @stmt_asm // "asm" statement or the body of an asm function +| 15 = @stmt_try_block +| 16 = @stmt_microsoft_try // Microsoft +| 17 = @stmt_decl +| 18 = @stmt_set_vla_size // C99 +| 19 = @stmt_vla_decl // C99 +| 25 = @stmt_assigned_goto // GNU +| 26 = @stmt_empty +| 27 = @stmt_continue +| 28 = @stmt_break +| 29 = @stmt_range_based_for // C++11 +// ... 30 @stmt_at_autoreleasepool_block deprecated +// ... 31 @stmt_objc_for_in deprecated +// ... 32 @stmt_at_synchronized deprecated +| 33 = @stmt_handler +// ... 34 @stmt_finally_end deprecated +| 35 = @stmt_constexpr_if +| 37 = @stmt_co_return +; + +type_vla( + int type_id: @type ref, + int decl: @stmt_vla_decl ref +); + +variable_vla( + int var: @variable ref, + int decl: @stmt_vla_decl ref +); + +if_initialization( + unique int if_stmt: @stmt_if ref, + int init_id: @stmt ref +); + +if_then( + unique int if_stmt: @stmt_if ref, + int then_id: @stmt ref +); + +if_else( + unique int if_stmt: @stmt_if ref, + int else_id: @stmt ref +); + +constexpr_if_initialization( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int init_id: @stmt ref +); + +constexpr_if_then( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int then_id: @stmt ref +); + +constexpr_if_else( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int else_id: @stmt ref +); + +while_body( + unique int while_stmt: @stmt_while ref, + int body_id: @stmt ref +); + +do_body( + unique int do_stmt: @stmt_end_test_while ref, + int body_id: @stmt ref +); + +switch_initialization( + unique int switch_stmt: @stmt_switch ref, + int init_id: @stmt ref +); + +#keyset[switch_stmt, index] +switch_case( + int switch_stmt: @stmt_switch ref, + int index: int ref, + int case_id: @stmt_switch_case ref +); + +switch_body( + unique int switch_stmt: @stmt_switch ref, + int body_id: @stmt ref +); + +for_initialization( + unique int for_stmt: @stmt_for ref, + int init_id: @stmt ref +); + +for_condition( + unique int for_stmt: @stmt_for ref, + int condition_id: @expr ref +); + +for_update( + unique int for_stmt: @stmt_for ref, + int update_id: @expr ref +); + +for_body( + unique int for_stmt: @stmt_for ref, + int body_id: @stmt ref +); + +@stmtparent = @stmt | @expr_stmt ; +stmtparents( + unique int id: @stmt ref, + int index: int ref, + int parent: @stmtparent ref +); + +ishandler(unique int block: @stmt_block ref); + +@cfgnode = @stmt | @expr | @function | @initialiser ; + +stmt_decl_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl: @declaration ref +); + +stmt_decl_entry_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl_entry: @element ref +); + +@functionorblock = @function | @stmt_block; + +blockscope( + unique int block: @stmt_block ref, + int enclosing: @functionorblock ref +); + +@jump = @stmt_goto | @stmt_break | @stmt_continue; + +@jumporlabel = @jump | @stmt_label | @literal; + +jumpinfo( + unique int id: @jumporlabel ref, + string str: string ref, + int target: @stmt ref +); + +preprocdirects( + unique int id: @preprocdirect, + int kind: int ref, + int location: @location_default ref +); +case @preprocdirect.kind of + 0 = @ppd_if +| 1 = @ppd_ifdef +| 2 = @ppd_ifndef +| 3 = @ppd_elif +| 4 = @ppd_else +| 5 = @ppd_endif +| 6 = @ppd_plain_include +| 7 = @ppd_define +| 8 = @ppd_undef +| 9 = @ppd_line +| 10 = @ppd_error +| 11 = @ppd_pragma +| 12 = @ppd_objc_import +| 13 = @ppd_include_next +| 18 = @ppd_warning +; + +@ppd_include = @ppd_plain_include | @ppd_objc_import | @ppd_include_next; + +@ppd_branch = @ppd_if | @ppd_ifdef | @ppd_ifndef | @ppd_elif; + +preprocpair( + int begin : @ppd_branch ref, + int elseelifend : @preprocdirect ref +); + +preproctrue(int branch : @ppd_branch ref); +preprocfalse(int branch : @ppd_branch ref); + +preproctext( + unique int id: @preprocdirect ref, + string head: string ref, + string body: string ref +); + +includes( + unique int id: @ppd_include ref, + int included: @file ref +); + +link_targets( + unique int id: @link_target, + int binary: @file ref +); + +link_parent( + int element : @element ref, + int link_target : @link_target ref +); + +/* XML Files */ + +xmlEncoding(unique int id: @file ref, string encoding: string ref); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters + | @xmlelement + | @xmlcomment + | @xmlattribute + | @xmldtd + | @file + | @xmlnamespace; diff --git a/cpp/downgrades/23f7cbb88a4eb29f30c3490363dc201bc054c5ff/upgrade.properties b/cpp/downgrades/23f7cbb88a4eb29f30c3490363dc201bc054c5ff/upgrade.properties new file mode 100644 index 00000000000..d697a16a42f --- /dev/null +++ b/cpp/downgrades/23f7cbb88a4eb29f30c3490363dc201bc054c5ff/upgrade.properties @@ -0,0 +1,3 @@ +description: Add new builtin operations +compatibility: partial +exprs.rel: run exprs.qlo diff --git a/cpp/ql/lib/upgrades/19e31bf071f588bb7efd1e4d5a185ce4f6fbbd84/old.dbscheme b/cpp/ql/lib/upgrades/19e31bf071f588bb7efd1e4d5a185ce4f6fbbd84/old.dbscheme new file mode 100644 index 00000000000..19e31bf071f --- /dev/null +++ b/cpp/ql/lib/upgrades/19e31bf071f588bb7efd1e4d5a185ce4f6fbbd84/old.dbscheme @@ -0,0 +1,2115 @@ + +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * gcc -c f1.c f2.c f3.c + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + /** + * An invocation of the compiler. Note that more than one file may + * be compiled per invocation. For example, this command compiles + * three source files: + * + * gcc -c f1.c f2.c f3.c + */ + unique int id : @compilation, + string cwd : string ref +); + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * gcc -c f1.c f2.c f3.c + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | *path to extractor* + * 1 | `--mimic` + * 2 | `/usr/bin/gcc` + * 3 | `-c` + * 4 | f1.c + * 5 | f2.c + * 6 | f3.c + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * gcc -c f1.c f2.c f3.c + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | f1.c + * 1 | f2.c + * 2 | f3.c + * + * Note that even if those files `#include` headers, those headers + * do not appear as rows. + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + + +/** + * External data, loaded from CSV files during snapshot creation. See + * [Tutorial: Incorporating external data](https://help.semmle.com/wiki/display/SD/Tutorial%3A+Incorporating+external+data) + * for more information. + */ +externalData( + int id : @externalDataElement, + string path : string ref, + int column: int ref, + string value : string ref +); + +/** + * The source location of the snapshot. + */ +sourceLocationPrefix(string prefix : string ref); + +/** + * Information about packages that provide code used during compilation. + * The `id` is just a unique identifier. + * The `namespace` is typically the name of the package manager that + * provided the package (e.g. "dpkg" or "yum"). + * The `package_name` is the name of the package, and `version` is its + * version (as a string). + */ +external_packages( + unique int id: @external_package, + string namespace : string ref, + string package_name : string ref, + string version : string ref +); + +/** + * Holds if File `fileid` was provided by package `package`. + */ +header_to_external_package( + int fileid : @file ref, + int package : @external_package ref +); + +/* + * Version history + */ + +svnentries( + unique int id : @svnentry, + string revision : string ref, + string author : string ref, + date revisionDate : date ref, + int changeSize : int ref +) + +svnaffectedfiles( + int id : @svnentry ref, + int file : @file ref, + string action : string ref +) + +svnentrymsg( + unique int id : @svnentry ref, + string message : string ref +) + +svnchurn( + int commit : @svnentry ref, + int file : @file ref, + int addedLines : int ref, + int deletedLines : int ref +) + +/* + * C++ dbscheme + */ + +@location = @location_stmt | @location_expr | @location_default ; + +/** + * The location of an element that is not an expression or a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ +locations_default( + /** The location of an element that is not an expression or a statement. */ + unique int id: @location_default, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ +locations_stmt( + /** The location of a statement. */ + unique int id: @location_stmt, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of an expression. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ +locations_expr( + /** The location of an expression. */ + unique int id: @location_expr, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** An element for which line-count information is available. */ +@sourceline = @file | @function | @variable | @enumconstant | @xmllocatable; + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +files( + unique int id: @file, + string name: string ref +); + +folders( + unique int id: @folder, + string name: string ref +); + +@container = @folder | @file + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +fileannotations( + int id: @file ref, + int kind: int ref, + string name: string ref, + string value: string ref +); + +inmacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +affectedbymacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +/* + case @macroinvocations.kind of + 1 = macro expansion + | 2 = other macro reference + ; +*/ +macroinvocations( + unique int id: @macroinvocation, + int macro_id: @ppd_define ref, + int location: @location_default ref, + int kind: int ref +); + +macroparent( + unique int id: @macroinvocation ref, + int parent_id: @macroinvocation ref +); + +// a macroinvocation may be part of another location +// the way to find a constant expression that uses a macro +// is thus to find a constant expression that has a location +// to which a macro invocation is bound +macrolocationbind( + int id: @macroinvocation ref, + int location: @location ref +); + +#keyset[invocation, argument_index] +macro_argument_unexpanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +#keyset[invocation, argument_index] +macro_argument_expanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +/* + case @function.kind of + 1 = normal + | 2 = constructor + | 3 = destructor + | 4 = conversion + | 5 = operator + | 6 = builtin // GCC built-in functions, e.g. __builtin___memcpy_chk + ; +*/ +functions( + unique int id: @function, + string name: string ref, + int kind: int ref +); + +function_entry_point(int id: @function ref, unique int entry_point: @stmt ref); + +function_return_type(int id: @function ref, int return_type: @type ref); + +/** If `function` is a coroutine, then this gives the + std::experimental::resumable_traits instance associated with it, + and the variables representing the `handle` and `promise` for it. */ +coroutine( + unique int function: @function ref, + int traits: @type ref, + int handle: @variable ref, + int promise: @variable ref +); + +/** The `new` function used for allocating the coroutine state, if any. */ +coroutine_new( + unique int function: @function ref, + int new: @function ref +); + +/** The `delete` function used for deallocating the coroutine state, if any. */ +coroutine_delete( + unique int function: @function ref, + int delete: @function ref +); + +purefunctions(unique int id: @function ref); + +function_deleted(unique int id: @function ref); + +function_defaulted(unique int id: @function ref); + +member_function_this_type(unique int id: @function ref, int this_type: @type ref); + +#keyset[id, type_id] +fun_decls( + int id: @fun_decl, + int function: @function ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +fun_def(unique int id: @fun_decl ref); +fun_specialized(unique int id: @fun_decl ref); +fun_implicit(unique int id: @fun_decl ref); +fun_decl_specifiers( + int id: @fun_decl ref, + string name: string ref +) +#keyset[fun_decl, index] +fun_decl_throws( + int fun_decl: @fun_decl ref, + int index: int ref, + int type_id: @type ref +); +/* an empty throw specification is different from none */ +fun_decl_empty_throws(unique int fun_decl: @fun_decl ref); +fun_decl_noexcept( + int fun_decl: @fun_decl ref, + int constant: @expr ref +); +fun_decl_empty_noexcept(int fun_decl: @fun_decl ref); +fun_decl_typedef_type( + unique int fun_decl: @fun_decl ref, + int typedeftype_id: @usertype ref +); + +param_decl_bind( + unique int id: @var_decl ref, + int index: int ref, + int fun_decl: @fun_decl ref +); + +#keyset[id, type_id] +var_decls( + int id: @var_decl, + int variable: @variable ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +var_def(unique int id: @var_decl ref); +var_decl_specifiers( + int id: @var_decl ref, + string name: string ref +) +is_structured_binding(unique int id: @variable ref); + +type_decls( + unique int id: @type_decl, + int type_id: @type ref, + int location: @location_default ref +); +type_def(unique int id: @type_decl ref); +type_decl_top( + unique int type_decl: @type_decl ref +); + +namespace_decls( + unique int id: @namespace_decl, + int namespace_id: @namespace ref, + int location: @location_default ref, + int bodylocation: @location_default ref +); + +usings( + unique int id: @using, + int element_id: @element ref, + int location: @location_default ref +); + +/** The element which contains the `using` declaration. */ +using_container( + int parent: @element ref, + int child: @using ref +); + +static_asserts( + unique int id: @static_assert, + int condition : @expr ref, + string message : string ref, + int location: @location_default ref, + int enclosing : @element ref +); + +// each function has an ordered list of parameters +#keyset[id, type_id] +#keyset[function, index, type_id] +params( + int id: @parameter, + int function: @functionorblock ref, + int index: int ref, + int type_id: @type ref +); + +overrides(int new: @function ref, int old: @function ref); + +#keyset[id, type_id] +membervariables( + int id: @membervariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +globalvariables( + int id: @globalvariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +localvariables( + int id: @localvariable, + int type_id: @type ref, + string name: string ref +); + +autoderivation( + unique int var: @variable ref, + int derivation_type: @type ref +); + +enumconstants( + unique int id: @enumconstant, + int parent: @usertype ref, + int index: int ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); + +@variable = @localscopevariable | @globalvariable | @membervariable; + +@localscopevariable = @localvariable | @parameter; + +/* + Built-in types are the fundamental types, e.g., integral, floating, and void. + + case @builtintype.kind of + 1 = error + | 2 = unknown + | 3 = void + | 4 = boolean + | 5 = char + | 6 = unsigned_char + | 7 = signed_char + | 8 = short + | 9 = unsigned_short + | 10 = signed_short + | 11 = int + | 12 = unsigned_int + | 13 = signed_int + | 14 = long + | 15 = unsigned_long + | 16 = signed_long + | 17 = long_long + | 18 = unsigned_long_long + | 19 = signed_long_long + | 20 = __int8 // Microsoft-specific + | 21 = __int16 // Microsoft-specific + | 22 = __int32 // Microsoft-specific + | 23 = __int64 // Microsoft-specific + | 24 = float + | 25 = double + | 26 = long_double + | 27 = _Complex_float // C99-specific + | 28 = _Complex_double // C99-specific + | 29 = _Complex_long double // C99-specific + | 30 = _Imaginary_float // C99-specific + | 31 = _Imaginary_double // C99-specific + | 32 = _Imaginary_long_double // C99-specific + | 33 = wchar_t // Microsoft-specific + | 34 = decltype_nullptr // C++11 + | 35 = __int128 + | 36 = unsigned___int128 + | 37 = signed___int128 + | 38 = __float128 + | 39 = _Complex___float128 + | 40 = _Decimal32 + | 41 = _Decimal64 + | 42 = _Decimal128 + | 43 = char16_t + | 44 = char32_t + | 45 = _Float32 + | 46 = _Float32x + | 47 = _Float64 + | 48 = _Float64x + | 49 = _Float128 + | 50 = _Float128x + | 51 = char8_t + ; +*/ +builtintypes( + unique int id: @builtintype, + string name: string ref, + int kind: int ref, + int size: int ref, + int sign: int ref, + int alignment: int ref +); + +/* + Derived types are types that are directly derived from existing types and + point to, refer to, transform type data to return a new type. + + case @derivedtype.kind of + 1 = pointer + | 2 = reference + | 3 = type_with_specifiers + | 4 = array + | 5 = gnu_vector + | 6 = routineptr + | 7 = routinereference + | 8 = rvalue_reference // C++11 +// ... 9 type_conforming_to_protocols deprecated + | 10 = block + ; +*/ +derivedtypes( + unique int id: @derivedtype, + string name: string ref, + int kind: int ref, + int type_id: @type ref +); + +pointerishsize(unique int id: @derivedtype ref, + int size: int ref, + int alignment: int ref); + +arraysizes( + unique int id: @derivedtype ref, + int num_elements: int ref, + int bytesize: int ref, + int alignment: int ref +); + +typedefbase( + unique int id: @usertype ref, + int type_id: @type ref +); + +/** + * An instance of the C++11 `decltype` operator. For example: + * ``` + * int a; + * decltype(1+a) b; + * ``` + * Here `expr` is `1+a`. + * + * Sometimes an additional pair of parentheses around the expression + * would change the semantics of this decltype, e.g. + * ``` + * struct A { double x; }; + * const A* a = new A(); + * decltype( a->x ); // type is double + * decltype((a->x)); // type is const double& + * ``` + * (Please consult the C++11 standard for more details). + * `parentheses_would_change_meaning` is `true` iff that is the case. + */ +#keyset[id, expr] +decltypes( + int id: @decltype, + int expr: @expr ref, + int base_type: @type ref, + boolean parentheses_would_change_meaning: boolean ref +); + +/* + case @usertype.kind of + 1 = struct + | 2 = class + | 3 = union + | 4 = enum + | 5 = typedef // classic C: typedef typedef type name + | 6 = template + | 7 = template_parameter + | 8 = template_template_parameter + | 9 = proxy_class // a proxy class associated with a template parameter +// ... 10 objc_class deprecated +// ... 11 objc_protocol deprecated +// ... 12 objc_category deprecated + | 13 = scoped_enum + | 14 = using_alias // a using name = type style typedef + ; +*/ +usertypes( + unique int id: @usertype, + string name: string ref, + int kind: int ref +); + +usertypesize( + unique int id: @usertype ref, + int size: int ref, + int alignment: int ref +); + +usertype_final(unique int id: @usertype ref); + +usertype_uuid( + unique int id: @usertype ref, + string uuid: string ref +); + +mangled_name( + unique int id: @declaration ref, + int mangled_name : @mangledname +); + +is_pod_class(unique int id: @usertype ref); +is_standard_layout_class(unique int id: @usertype ref); + +is_complete(unique int id: @usertype ref); + +is_class_template(unique int id: @usertype ref); +class_instantiation( + int to: @usertype ref, + int from: @usertype ref +); +class_template_argument( + int type_id: @usertype ref, + int index: int ref, + int arg_type: @type ref +); +class_template_argument_value( + int type_id: @usertype ref, + int index: int ref, + int arg_value: @expr ref +); + +is_proxy_class_for( + unique int id: @usertype ref, + unique int templ_param_id: @usertype ref +); + +type_mentions( + unique int id: @type_mention, + int type_id: @type ref, + int location: @location ref, + // a_symbol_reference_kind from the EDG frontend. See symbol_ref.h there. + int kind: int ref +); + +is_function_template(unique int id: @function ref); +function_instantiation( + unique int to: @function ref, + int from: @function ref +); +function_template_argument( + int function_id: @function ref, + int index: int ref, + int arg_type: @type ref +); +function_template_argument_value( + int function_id: @function ref, + int index: int ref, + int arg_value: @expr ref +); + +is_variable_template(unique int id: @variable ref); +variable_instantiation( + unique int to: @variable ref, + int from: @variable ref +); +variable_template_argument( + int variable_id: @variable ref, + int index: int ref, + int arg_type: @type ref +); +variable_template_argument_value( + int variable_id: @variable ref, + int index: int ref, + int arg_value: @expr ref +); + +/* + Fixed point types + precision(1) = short, precision(2) = default, precision(3) = long + is_unsigned(1) = unsigned is_unsigned(2) = signed + is_fract_type(1) = declared with _Fract + saturating(1) = declared with _Sat +*/ +/* TODO +fixedpointtypes( + unique int id: @fixedpointtype, + int precision: int ref, + int is_unsigned: int ref, + int is_fract_type: int ref, + int saturating: int ref); +*/ + +routinetypes( + unique int id: @routinetype, + int return_type: @type ref +); + +routinetypeargs( + int routine: @routinetype ref, + int index: int ref, + int type_id: @type ref +); + +ptrtomembers( + unique int id: @ptrtomember, + int type_id: @type ref, + int class_id: @type ref +); + +/* + specifiers for types, functions, and variables + + "public", + "protected", + "private", + + "const", + "volatile", + "static", + + "pure", + "virtual", + "sealed", // Microsoft + "__interface", // Microsoft + "inline", + "explicit", + + "near", // near far extension + "far", // near far extension + "__ptr32", // Microsoft + "__ptr64", // Microsoft + "__sptr", // Microsoft + "__uptr", // Microsoft + "dllimport", // Microsoft + "dllexport", // Microsoft + "thread", // Microsoft + "naked", // Microsoft + "microsoft_inline", // Microsoft + "forceinline", // Microsoft + "selectany", // Microsoft + "nothrow", // Microsoft + "novtable", // Microsoft + "noreturn", // Microsoft + "noinline", // Microsoft + "noalias", // Microsoft + "restrict", // Microsoft +*/ + +specifiers( + unique int id: @specifier, + unique string str: string ref +); + +typespecifiers( + int type_id: @type ref, + int spec_id: @specifier ref +); + +funspecifiers( + int func_id: @function ref, + int spec_id: @specifier ref +); + +varspecifiers( + int var_id: @accessible ref, + int spec_id: @specifier ref +); + +attributes( + unique int id: @attribute, + int kind: int ref, + string name: string ref, + string name_space: string ref, + int location: @location_default ref +); + +case @attribute.kind of + 0 = @gnuattribute +| 1 = @stdattribute +| 2 = @declspec +| 3 = @msattribute +| 4 = @alignas +// ... 5 @objc_propertyattribute deprecated +; + +attribute_args( + unique int id: @attribute_arg, + int kind: int ref, + int attribute: @attribute ref, + int index: int ref, + int location: @location_default ref +); + +case @attribute_arg.kind of + 0 = @attribute_arg_empty +| 1 = @attribute_arg_token +| 2 = @attribute_arg_constant +| 3 = @attribute_arg_type +; + +attribute_arg_value( + unique int arg: @attribute_arg ref, + string value: string ref +); +attribute_arg_type( + unique int arg: @attribute_arg ref, + int type_id: @type ref +); +attribute_arg_name( + unique int arg: @attribute_arg ref, + string name: string ref +); + +typeattributes( + int type_id: @type ref, + int spec_id: @attribute ref +); + +funcattributes( + int func_id: @function ref, + int spec_id: @attribute ref +); + +varattributes( + int var_id: @accessible ref, + int spec_id: @attribute ref +); + +stmtattributes( + int stmt_id: @stmt ref, + int spec_id: @attribute ref +); + +@type = @builtintype + | @derivedtype + | @usertype + /* TODO | @fixedpointtype */ + | @routinetype + | @ptrtomember + | @decltype; + +unspecifiedtype( + unique int type_id: @type ref, + int unspecified_type_id: @type ref +); + +member( + int parent: @type ref, + int index: int ref, + int child: @member ref +); + +@enclosingfunction_child = @usertype | @variable | @namespace + +enclosingfunction( + unique int child: @enclosingfunction_child ref, + int parent: @function ref +); + +derivations( + unique int derivation: @derivation, + int sub: @type ref, + int index: int ref, + int super: @type ref, + int location: @location_default ref +); + +derspecifiers( + int der_id: @derivation ref, + int spec_id: @specifier ref +); + +/** + * Contains the byte offset of the base class subobject within the derived + * class. Only holds for non-virtual base classes, but see table + * `virtual_base_offsets` for offsets of virtual base class subobjects. + */ +direct_base_offsets( + unique int der_id: @derivation ref, + int offset: int ref +); + +/** + * Contains the byte offset of the virtual base class subobject for class + * `super` within a most-derived object of class `sub`. `super` can be either a + * direct or indirect base class. + */ +#keyset[sub, super] +virtual_base_offsets( + int sub: @usertype ref, + int super: @usertype ref, + int offset: int ref +); + +frienddecls( + unique int id: @frienddecl, + int type_id: @type ref, + int decl_id: @declaration ref, + int location: @location_default ref +); + +@declaredtype = @usertype ; + +@declaration = @function + | @declaredtype + | @variable + | @enumconstant + | @frienddecl; + +@member = @membervariable + | @function + | @declaredtype + | @enumconstant; + +@locatable = @diagnostic + | @declaration + | @ppd_include + | @ppd_define + | @macroinvocation + /*| @funcall*/ + | @xmllocatable + | @attribute + | @attribute_arg; + +@namedscope = @namespace | @usertype; + +@element = @locatable + | @file + | @folder + | @specifier + | @type + | @expr + | @namespace + | @initialiser + | @stmt + | @derivation + | @comment + | @preprocdirect + | @fun_decl + | @var_decl + | @type_decl + | @namespace_decl + | @using + | @namequalifier + | @specialnamequalifyingelement + | @static_assert + | @type_mention + | @lambdacapture; + +@exprparent = @element; + +comments( + unique int id: @comment, + string contents: string ref, + int location: @location_default ref +); + +commentbinding( + int id: @comment ref, + int element: @element ref +); + +exprconv( + int converted: @expr ref, + unique int conversion: @expr ref +); + +compgenerated(unique int id: @element ref); + +/** + * `destructor_call` destructs the `i`'th entity that should be + * destructed following `element`. Note that entities should be + * destructed in reverse construction order, so for a given `element` + * these should be called from highest to lowest `i`. + */ +#keyset[element, destructor_call] +#keyset[element, i] +synthetic_destructor_call( + int element: @element ref, + int i: int ref, + int destructor_call: @routineexpr ref +); + +namespaces( + unique int id: @namespace, + string name: string ref +); + +namespace_inline( + unique int id: @namespace ref +); + +namespacembrs( + int parentid: @namespace ref, + unique int memberid: @namespacembr ref +); + +@namespacembr = @declaration | @namespace; + +exprparents( + int expr_id: @expr ref, + int child_index: int ref, + int parent_id: @exprparent ref +); + +expr_isload(unique int expr_id: @expr ref); + +@cast = @c_style_cast + | @const_cast + | @dynamic_cast + | @reinterpret_cast + | @static_cast + ; + +/* +case @conversion.kind of + 0 = @simple_conversion // a numeric conversion, qualification conversion, or a reinterpret_cast +| 1 = @bool_conversion // conversion to 'bool' +| 2 = @base_class_conversion // a derived-to-base conversion +| 3 = @derived_class_conversion // a base-to-derived conversion +| 4 = @pm_base_class_conversion // a derived-to-base conversion of a pointer to member +| 5 = @pm_derived_class_conversion // a base-to-derived conversion of a pointer to member +| 6 = @glvalue_adjust // an adjustment of the type of a glvalue +| 7 = @prvalue_adjust // an adjustment of the type of a prvalue +; +*/ +/** + * Describes the semantics represented by a cast expression. This is largely + * independent of the source syntax of the cast, so it is separate from the + * regular expression kind. + */ +conversionkinds( + unique int expr_id: @cast ref, + int kind: int ref +); + +@conversion = @cast + | @array_to_pointer + | @parexpr + | @reference_to + | @ref_indirect + | @temp_init + ; + +/* +case @funbindexpr.kind of + 0 = @normal_call // a normal call +| 1 = @virtual_call // a virtual call +| 2 = @adl_call // a call whose target is only found by ADL +; +*/ +iscall(unique int caller: @funbindexpr ref, int kind: int ref); + +numtemplatearguments( + unique int expr_id: @expr ref, + int num: int ref +); + +specialnamequalifyingelements( + unique int id: @specialnamequalifyingelement, + unique string name: string ref +); + +@namequalifiableelement = @expr | @namequalifier; +@namequalifyingelement = @namespace + | @specialnamequalifyingelement + | @usertype; + +namequalifiers( + unique int id: @namequalifier, + unique int qualifiableelement: @namequalifiableelement ref, + int qualifyingelement: @namequalifyingelement ref, + int location: @location_default ref +); + +varbind( + int expr: @varbindexpr ref, + int var: @accessible ref +); + +funbind( + int expr: @funbindexpr ref, + int fun: @function ref +); + +@any_new_expr = @new_expr + | @new_array_expr; + +@new_or_delete_expr = @any_new_expr + | @delete_expr + | @delete_array_expr; + +@prefix_crement_expr = @preincrexpr | @predecrexpr; + +@postfix_crement_expr = @postincrexpr | @postdecrexpr; + +@increment_expr = @preincrexpr | @postincrexpr; + +@decrement_expr = @predecrexpr | @postdecrexpr; + +@crement_expr = @increment_expr | @decrement_expr; + +@un_arith_op_expr = @arithnegexpr + | @unaryplusexpr + | @conjugation + | @realpartexpr + | @imagpartexpr + | @crement_expr + ; + +@un_bitwise_op_expr = @complementexpr; + +@un_log_op_expr = @notexpr; + +@un_op_expr = @address_of + | @indirect + | @un_arith_op_expr + | @un_bitwise_op_expr + | @builtinaddressof + | @vec_fill + | @un_log_op_expr + | @co_await + | @co_yield + ; + +@bin_log_op_expr = @andlogicalexpr | @orlogicalexpr; + +@cmp_op_expr = @eq_op_expr | @rel_op_expr; + +@eq_op_expr = @eqexpr | @neexpr; + +@rel_op_expr = @gtexpr + | @ltexpr + | @geexpr + | @leexpr + | @spaceshipexpr + ; + +@bin_bitwise_op_expr = @lshiftexpr + | @rshiftexpr + | @andexpr + | @orexpr + | @xorexpr + ; + +@p_arith_op_expr = @paddexpr + | @psubexpr + | @pdiffexpr + ; + +@bin_arith_op_expr = @addexpr + | @subexpr + | @mulexpr + | @divexpr + | @remexpr + | @jmulexpr + | @jdivexpr + | @fjaddexpr + | @jfaddexpr + | @fjsubexpr + | @jfsubexpr + | @minexpr + | @maxexpr + | @p_arith_op_expr + ; + +@bin_op_expr = @bin_arith_op_expr + | @bin_bitwise_op_expr + | @cmp_op_expr + | @bin_log_op_expr + ; + +@op_expr = @un_op_expr + | @bin_op_expr + | @assign_expr + | @conditionalexpr + ; + +@assign_arith_expr = @assignaddexpr + | @assignsubexpr + | @assignmulexpr + | @assigndivexpr + | @assignremexpr + ; + +@assign_bitwise_expr = @assignandexpr + | @assignorexpr + | @assignxorexpr + | @assignlshiftexpr + | @assignrshiftexpr + | @assignpaddexpr + | @assignpsubexpr + ; + +@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr + +@assign_expr = @assignexpr | @assign_op_expr + +/* + case @allocator.form of + 0 = plain + | 1 = alignment + ; +*/ + +/** + * The allocator function associated with a `new` or `new[]` expression. + * The `form` column specified whether the allocation call contains an alignment + * argument. + */ +expr_allocator( + unique int expr: @any_new_expr ref, + int func: @function ref, + int form: int ref +); + +/* + case @deallocator.form of + 0 = plain + | 1 = size + | 2 = alignment + | 3 = size_and_alignment + ; +*/ + +/** + * The deallocator function associated with a `delete`, `delete[]`, `new`, or + * `new[]` expression. For a `new` or `new[]` expression, the deallocator is the + * one used to free memory if the initialization throws an exception. + * The `form` column specifies whether the deallocation call contains a size + * argument, and alignment argument, or both. + */ +expr_deallocator( + unique int expr: @new_or_delete_expr ref, + int func: @function ref, + int form: int ref +); + +/** + * Holds if the `@conditionalexpr` is of the two operand form + * `guard ? : false`. + */ +expr_cond_two_operand( + unique int cond: @conditionalexpr ref +); + +/** + * The guard of `@conditionalexpr` `guard ? true : false` + */ +expr_cond_guard( + unique int cond: @conditionalexpr ref, + int guard: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` holds. For the two operand form + * `guard ?: false` consider using `expr_cond_guard` instead. + */ +expr_cond_true( + unique int cond: @conditionalexpr ref, + int true: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` does not hold. + */ +expr_cond_false( + unique int cond: @conditionalexpr ref, + int false: @expr ref +); + +/** A string representation of the value. */ +values( + unique int id: @value, + string str: string ref +); + +/** The actual text in the source code for the value, if any. */ +valuetext( + unique int id: @value ref, + string text: string ref +); + +valuebind( + int val: @value ref, + unique int expr: @expr ref +); + +fieldoffsets( + unique int id: @variable ref, + int byteoffset: int ref, + int bitoffset: int ref +); + +bitfield( + unique int id: @variable ref, + int bits: int ref, + int declared_bits: int ref +); + +/* TODO +memberprefix( + int member: @expr ref, + int prefix: @expr ref +); +*/ + +/* + kind(1) = mbrcallexpr + kind(2) = mbrptrcallexpr + kind(3) = mbrptrmbrcallexpr + kind(4) = ptrmbrptrmbrcallexpr + kind(5) = mbrreadexpr // x.y + kind(6) = mbrptrreadexpr // p->y + kind(7) = mbrptrmbrreadexpr // x.*pm + kind(8) = mbrptrmbrptrreadexpr // x->*pm + kind(9) = staticmbrreadexpr // static x.y + kind(10) = staticmbrptrreadexpr // static p->y +*/ +/* TODO +memberaccess( + int member: @expr ref, + int kind: int ref +); +*/ + +initialisers( + unique int init: @initialiser, + int var: @accessible ref, + unique int expr: @expr ref, + int location: @location_expr ref +); + +braced_initialisers( + int init: @initialiser ref +); + +/** + * An ancestor for the expression, for cases in which we cannot + * otherwise find the expression's parent. + */ +expr_ancestor( + int exp: @expr ref, + int ancestor: @element ref +); + +exprs( + unique int id: @expr, + int kind: int ref, + int location: @location_expr ref +); + +/* + case @value.category of + 1 = prval + | 2 = xval + | 3 = lval + ; +*/ +expr_types( + int id: @expr ref, + int typeid: @type ref, + int value_category: int ref +); + +case @expr.kind of + 1 = @errorexpr +| 2 = @address_of // & AddressOfExpr +| 3 = @reference_to // ReferenceToExpr (implicit?) +| 4 = @indirect // * PointerDereferenceExpr +| 5 = @ref_indirect // ReferenceDereferenceExpr (implicit?) +// ... +| 8 = @array_to_pointer // (???) +| 9 = @vacuous_destructor_call // VacuousDestructorCall +// ... +| 11 = @assume // Microsoft +| 12 = @parexpr +| 13 = @arithnegexpr +| 14 = @unaryplusexpr +| 15 = @complementexpr +| 16 = @notexpr +| 17 = @conjugation // GNU ~ operator +| 18 = @realpartexpr // GNU __real +| 19 = @imagpartexpr // GNU __imag +| 20 = @postincrexpr +| 21 = @postdecrexpr +| 22 = @preincrexpr +| 23 = @predecrexpr +| 24 = @conditionalexpr +| 25 = @addexpr +| 26 = @subexpr +| 27 = @mulexpr +| 28 = @divexpr +| 29 = @remexpr +| 30 = @jmulexpr // C99 mul imaginary +| 31 = @jdivexpr // C99 div imaginary +| 32 = @fjaddexpr // C99 add real + imaginary +| 33 = @jfaddexpr // C99 add imaginary + real +| 34 = @fjsubexpr // C99 sub real - imaginary +| 35 = @jfsubexpr // C99 sub imaginary - real +| 36 = @paddexpr // pointer add (pointer + int or int + pointer) +| 37 = @psubexpr // pointer sub (pointer - integer) +| 38 = @pdiffexpr // difference between two pointers +| 39 = @lshiftexpr +| 40 = @rshiftexpr +| 41 = @andexpr +| 42 = @orexpr +| 43 = @xorexpr +| 44 = @eqexpr +| 45 = @neexpr +| 46 = @gtexpr +| 47 = @ltexpr +| 48 = @geexpr +| 49 = @leexpr +| 50 = @minexpr // GNU minimum +| 51 = @maxexpr // GNU maximum +| 52 = @assignexpr +| 53 = @assignaddexpr +| 54 = @assignsubexpr +| 55 = @assignmulexpr +| 56 = @assigndivexpr +| 57 = @assignremexpr +| 58 = @assignlshiftexpr +| 59 = @assignrshiftexpr +| 60 = @assignandexpr +| 61 = @assignorexpr +| 62 = @assignxorexpr +| 63 = @assignpaddexpr // assign pointer add +| 64 = @assignpsubexpr // assign pointer sub +| 65 = @andlogicalexpr +| 66 = @orlogicalexpr +| 67 = @commaexpr +| 68 = @subscriptexpr // access to member of an array, e.g., a[5] +// ... 69 @objc_subscriptexpr deprecated +// ... 70 @cmdaccess deprecated +// ... +| 73 = @virtfunptrexpr +| 74 = @callexpr +// ... 75 @msgexpr_normal deprecated +// ... 76 @msgexpr_super deprecated +// ... 77 @atselectorexpr deprecated +// ... 78 @atprotocolexpr deprecated +| 79 = @vastartexpr +| 80 = @vaargexpr +| 81 = @vaendexpr +| 82 = @vacopyexpr +// ... 83 @atencodeexpr deprecated +| 84 = @varaccess +| 85 = @thisaccess +// ... 86 @objc_box_expr deprecated +| 87 = @new_expr +| 88 = @delete_expr +| 89 = @throw_expr +| 90 = @condition_decl // a variable declared in a condition, e.g., if(int x = y > 2) +| 91 = @braced_init_list +| 92 = @type_id +| 93 = @runtime_sizeof +| 94 = @runtime_alignof +| 95 = @sizeof_pack +| 96 = @expr_stmt // GNU extension +| 97 = @routineexpr +| 98 = @type_operand // used to access a type in certain contexts (haven't found any examples yet....) +| 99 = @offsetofexpr // offsetof ::= type and field +| 100 = @hasassignexpr // __has_assign ::= type +| 101 = @hascopyexpr // __has_copy ::= type +| 102 = @hasnothrowassign // __has_nothrow_assign ::= type +| 103 = @hasnothrowconstr // __has_nothrow_constructor ::= type +| 104 = @hasnothrowcopy // __has_nothrow_copy ::= type +| 105 = @hastrivialassign // __has_trivial_assign ::= type +| 106 = @hastrivialconstr // __has_trivial_constructor ::= type +| 107 = @hastrivialcopy // __has_trivial_copy ::= type +| 108 = @hasuserdestr // __has_user_destructor ::= type +| 109 = @hasvirtualdestr // __has_virtual_destructor ::= type +| 110 = @isabstractexpr // __is_abstract ::= type +| 111 = @isbaseofexpr // __is_base_of ::= type type +| 112 = @isclassexpr // __is_class ::= type +| 113 = @isconvtoexpr // __is_convertible_to ::= type type +| 114 = @isemptyexpr // __is_empty ::= type +| 115 = @isenumexpr // __is_enum ::= type +| 116 = @ispodexpr // __is_pod ::= type +| 117 = @ispolyexpr // __is_polymorphic ::= type +| 118 = @isunionexpr // __is_union ::= type +| 119 = @typescompexpr // GNU __builtin_types_compatible ::= type type +| 120 = @intaddrexpr // EDG internal builtin, used to implement offsetof +// ... +| 122 = @hastrivialdestructor // __has_trivial_destructor ::= type +| 123 = @literal +| 124 = @uuidof +| 127 = @aggregateliteral +| 128 = @delete_array_expr +| 129 = @new_array_expr +// ... 130 @objc_array_literal deprecated +// ... 131 @objc_dictionary_literal deprecated +| 132 = @foldexpr +// ... +| 200 = @ctordirectinit +| 201 = @ctorvirtualinit +| 202 = @ctorfieldinit +| 203 = @ctordelegatinginit +| 204 = @dtordirectdestruct +| 205 = @dtorvirtualdestruct +| 206 = @dtorfielddestruct +// ... +| 210 = @static_cast +| 211 = @reinterpret_cast +| 212 = @const_cast +| 213 = @dynamic_cast +| 214 = @c_style_cast +| 215 = @lambdaexpr +| 216 = @param_ref +| 217 = @noopexpr +// ... +| 294 = @istriviallyconstructibleexpr +| 295 = @isdestructibleexpr +| 296 = @isnothrowdestructibleexpr +| 297 = @istriviallydestructibleexpr +| 298 = @istriviallyassignableexpr +| 299 = @isnothrowassignableexpr +| 300 = @istrivialexpr +| 301 = @isstandardlayoutexpr +| 302 = @istriviallycopyableexpr +| 303 = @isliteraltypeexpr +| 304 = @hastrivialmoveconstructorexpr +| 305 = @hastrivialmoveassignexpr +| 306 = @hasnothrowmoveassignexpr +| 307 = @isconstructibleexpr +| 308 = @isnothrowconstructibleexpr +| 309 = @hasfinalizerexpr +| 310 = @isdelegateexpr +| 311 = @isinterfaceclassexpr +| 312 = @isrefarrayexpr +| 313 = @isrefclassexpr +| 314 = @issealedexpr +| 315 = @issimplevalueclassexpr +| 316 = @isvalueclassexpr +| 317 = @isfinalexpr +| 319 = @noexceptexpr +| 320 = @builtinshufflevector +| 321 = @builtinchooseexpr +| 322 = @builtinaddressof +| 323 = @vec_fill +| 324 = @builtinconvertvector +| 325 = @builtincomplex +| 326 = @spaceshipexpr +| 327 = @co_await +| 328 = @co_yield +| 329 = @temp_init +; + +@var_args_expr = @vastartexpr + | @vaendexpr + | @vaargexpr + | @vacopyexpr + ; + +@builtin_op = @var_args_expr + | @noopexpr + | @offsetofexpr + | @intaddrexpr + | @hasassignexpr + | @hascopyexpr + | @hasnothrowassign + | @hasnothrowconstr + | @hasnothrowcopy + | @hastrivialassign + | @hastrivialconstr + | @hastrivialcopy + | @hastrivialdestructor + | @hasuserdestr + | @hasvirtualdestr + | @isabstractexpr + | @isbaseofexpr + | @isclassexpr + | @isconvtoexpr + | @isemptyexpr + | @isenumexpr + | @ispodexpr + | @ispolyexpr + | @isunionexpr + | @typescompexpr + | @builtinshufflevector + | @builtinconvertvector + | @builtinaddressof + | @istriviallyconstructibleexpr + | @isdestructibleexpr + | @isnothrowdestructibleexpr + | @istriviallydestructibleexpr + | @istriviallyassignableexpr + | @isnothrowassignableexpr + | @isstandardlayoutexpr + | @istriviallycopyableexpr + | @isliteraltypeexpr + | @hastrivialmoveconstructorexpr + | @hastrivialmoveassignexpr + | @hasnothrowmoveassignexpr + | @isconstructibleexpr + | @isnothrowconstructibleexpr + | @hasfinalizerexpr + | @isdelegateexpr + | @isinterfaceclassexpr + | @isrefarrayexpr + | @isrefclassexpr + | @issealedexpr + | @issimplevalueclassexpr + | @isvalueclassexpr + | @isfinalexpr + | @builtinchooseexpr + | @builtincomplex + ; + +new_allocated_type( + unique int expr: @new_expr ref, + int type_id: @type ref +); + +new_array_allocated_type( + unique int expr: @new_array_expr ref, + int type_id: @type ref +); + +/** + * The field being initialized by an initializer expression within an aggregate + * initializer for a class/struct/union. + */ +#keyset[aggregate, field] +aggregate_field_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int field: @membervariable ref +); + +/** + * The index of the element being initialized by an initializer expression + * within an aggregate initializer for an array. + */ +#keyset[aggregate, element_index] +aggregate_array_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int element_index: int ref +); + +@ctorinit = @ctordirectinit + | @ctorvirtualinit + | @ctorfieldinit + | @ctordelegatinginit; +@dtordestruct = @dtordirectdestruct + | @dtorvirtualdestruct + | @dtorfielddestruct; + + +condition_decl_bind( + unique int expr: @condition_decl ref, + unique int decl: @declaration ref +); + +typeid_bind( + unique int expr: @type_id ref, + int type_id: @type ref +); + +uuidof_bind( + unique int expr: @uuidof ref, + int type_id: @type ref +); + +@runtime_sizeof_or_alignof = @runtime_sizeof | @runtime_alignof; + +sizeof_bind( + unique int expr: @runtime_sizeof_or_alignof ref, + int type_id: @type ref +); + +code_block( + unique int block: @literal ref, + unique int routine: @function ref +); + +lambdas( + unique int expr: @lambdaexpr ref, + string default_capture: string ref, + boolean has_explicit_return_type: boolean ref +); + +lambda_capture( + unique int id: @lambdacapture, + int lambda: @lambdaexpr ref, + int index: int ref, + int field: @membervariable ref, + boolean captured_by_reference: boolean ref, + boolean is_implicit: boolean ref, + int location: @location_default ref +); + +@funbindexpr = @routineexpr + | @new_expr + | @delete_expr + | @delete_array_expr + | @ctordirectinit + | @ctorvirtualinit + | @ctordelegatinginit + | @dtordirectdestruct + | @dtorvirtualdestruct; + +@varbindexpr = @varaccess | @ctorfieldinit | @dtorfielddestruct; +@addressable = @function | @variable ; +@accessible = @addressable | @enumconstant ; + +@access = @varaccess | @routineexpr ; + +fold( + int expr: @foldexpr ref, + string operator: string ref, + boolean is_left_fold: boolean ref +); + +stmts( + unique int id: @stmt, + int kind: int ref, + int location: @location_stmt ref +); + +case @stmt.kind of + 1 = @stmt_expr +| 2 = @stmt_if +| 3 = @stmt_while +| 4 = @stmt_goto +| 5 = @stmt_label +| 6 = @stmt_return +| 7 = @stmt_block +| 8 = @stmt_end_test_while // do { ... } while ( ... ) +| 9 = @stmt_for +| 10 = @stmt_switch_case +| 11 = @stmt_switch +| 13 = @stmt_asm // "asm" statement or the body of an asm function +| 15 = @stmt_try_block +| 16 = @stmt_microsoft_try // Microsoft +| 17 = @stmt_decl +| 18 = @stmt_set_vla_size // C99 +| 19 = @stmt_vla_decl // C99 +| 25 = @stmt_assigned_goto // GNU +| 26 = @stmt_empty +| 27 = @stmt_continue +| 28 = @stmt_break +| 29 = @stmt_range_based_for // C++11 +// ... 30 @stmt_at_autoreleasepool_block deprecated +// ... 31 @stmt_objc_for_in deprecated +// ... 32 @stmt_at_synchronized deprecated +| 33 = @stmt_handler +// ... 34 @stmt_finally_end deprecated +| 35 = @stmt_constexpr_if +| 37 = @stmt_co_return +; + +type_vla( + int type_id: @type ref, + int decl: @stmt_vla_decl ref +); + +variable_vla( + int var: @variable ref, + int decl: @stmt_vla_decl ref +); + +if_initialization( + unique int if_stmt: @stmt_if ref, + int init_id: @stmt ref +); + +if_then( + unique int if_stmt: @stmt_if ref, + int then_id: @stmt ref +); + +if_else( + unique int if_stmt: @stmt_if ref, + int else_id: @stmt ref +); + +constexpr_if_initialization( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int init_id: @stmt ref +); + +constexpr_if_then( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int then_id: @stmt ref +); + +constexpr_if_else( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int else_id: @stmt ref +); + +while_body( + unique int while_stmt: @stmt_while ref, + int body_id: @stmt ref +); + +do_body( + unique int do_stmt: @stmt_end_test_while ref, + int body_id: @stmt ref +); + +switch_initialization( + unique int switch_stmt: @stmt_switch ref, + int init_id: @stmt ref +); + +#keyset[switch_stmt, index] +switch_case( + int switch_stmt: @stmt_switch ref, + int index: int ref, + int case_id: @stmt_switch_case ref +); + +switch_body( + unique int switch_stmt: @stmt_switch ref, + int body_id: @stmt ref +); + +for_initialization( + unique int for_stmt: @stmt_for ref, + int init_id: @stmt ref +); + +for_condition( + unique int for_stmt: @stmt_for ref, + int condition_id: @expr ref +); + +for_update( + unique int for_stmt: @stmt_for ref, + int update_id: @expr ref +); + +for_body( + unique int for_stmt: @stmt_for ref, + int body_id: @stmt ref +); + +@stmtparent = @stmt | @expr_stmt ; +stmtparents( + unique int id: @stmt ref, + int index: int ref, + int parent: @stmtparent ref +); + +ishandler(unique int block: @stmt_block ref); + +@cfgnode = @stmt | @expr | @function | @initialiser ; + +stmt_decl_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl: @declaration ref +); + +stmt_decl_entry_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl_entry: @element ref +); + +@functionorblock = @function | @stmt_block; + +blockscope( + unique int block: @stmt_block ref, + int enclosing: @functionorblock ref +); + +@jump = @stmt_goto | @stmt_break | @stmt_continue; + +@jumporlabel = @jump | @stmt_label | @literal; + +jumpinfo( + unique int id: @jumporlabel ref, + string str: string ref, + int target: @stmt ref +); + +preprocdirects( + unique int id: @preprocdirect, + int kind: int ref, + int location: @location_default ref +); +case @preprocdirect.kind of + 0 = @ppd_if +| 1 = @ppd_ifdef +| 2 = @ppd_ifndef +| 3 = @ppd_elif +| 4 = @ppd_else +| 5 = @ppd_endif +| 6 = @ppd_plain_include +| 7 = @ppd_define +| 8 = @ppd_undef +| 9 = @ppd_line +| 10 = @ppd_error +| 11 = @ppd_pragma +| 12 = @ppd_objc_import +| 13 = @ppd_include_next +| 18 = @ppd_warning +; + +@ppd_include = @ppd_plain_include | @ppd_objc_import | @ppd_include_next; + +@ppd_branch = @ppd_if | @ppd_ifdef | @ppd_ifndef | @ppd_elif; + +preprocpair( + int begin : @ppd_branch ref, + int elseelifend : @preprocdirect ref +); + +preproctrue(int branch : @ppd_branch ref); +preprocfalse(int branch : @ppd_branch ref); + +preproctext( + unique int id: @preprocdirect ref, + string head: string ref, + string body: string ref +); + +includes( + unique int id: @ppd_include ref, + int included: @file ref +); + +link_targets( + unique int id: @link_target, + int binary: @file ref +); + +link_parent( + int element : @element ref, + int link_target : @link_target ref +); + +/* XML Files */ + +xmlEncoding(unique int id: @file ref, string encoding: string ref); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters + | @xmlelement + | @xmlcomment + | @xmlattribute + | @xmldtd + | @file + | @xmlnamespace; diff --git a/cpp/ql/lib/upgrades/19e31bf071f588bb7efd1e4d5a185ce4f6fbbd84/semmlecode.cpp.dbscheme b/cpp/ql/lib/upgrades/19e31bf071f588bb7efd1e4d5a185ce4f6fbbd84/semmlecode.cpp.dbscheme new file mode 100644 index 00000000000..23f7cbb88a4 --- /dev/null +++ b/cpp/ql/lib/upgrades/19e31bf071f588bb7efd1e4d5a185ce4f6fbbd84/semmlecode.cpp.dbscheme @@ -0,0 +1,2125 @@ + +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * gcc -c f1.c f2.c f3.c + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + /** + * An invocation of the compiler. Note that more than one file may + * be compiled per invocation. For example, this command compiles + * three source files: + * + * gcc -c f1.c f2.c f3.c + */ + unique int id : @compilation, + string cwd : string ref +); + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * gcc -c f1.c f2.c f3.c + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | *path to extractor* + * 1 | `--mimic` + * 2 | `/usr/bin/gcc` + * 3 | `-c` + * 4 | f1.c + * 5 | f2.c + * 6 | f3.c + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * gcc -c f1.c f2.c f3.c + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | f1.c + * 1 | f2.c + * 2 | f3.c + * + * Note that even if those files `#include` headers, those headers + * do not appear as rows. + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + + +/** + * External data, loaded from CSV files during snapshot creation. See + * [Tutorial: Incorporating external data](https://help.semmle.com/wiki/display/SD/Tutorial%3A+Incorporating+external+data) + * for more information. + */ +externalData( + int id : @externalDataElement, + string path : string ref, + int column: int ref, + string value : string ref +); + +/** + * The source location of the snapshot. + */ +sourceLocationPrefix(string prefix : string ref); + +/** + * Information about packages that provide code used during compilation. + * The `id` is just a unique identifier. + * The `namespace` is typically the name of the package manager that + * provided the package (e.g. "dpkg" or "yum"). + * The `package_name` is the name of the package, and `version` is its + * version (as a string). + */ +external_packages( + unique int id: @external_package, + string namespace : string ref, + string package_name : string ref, + string version : string ref +); + +/** + * Holds if File `fileid` was provided by package `package`. + */ +header_to_external_package( + int fileid : @file ref, + int package : @external_package ref +); + +/* + * Version history + */ + +svnentries( + unique int id : @svnentry, + string revision : string ref, + string author : string ref, + date revisionDate : date ref, + int changeSize : int ref +) + +svnaffectedfiles( + int id : @svnentry ref, + int file : @file ref, + string action : string ref +) + +svnentrymsg( + unique int id : @svnentry ref, + string message : string ref +) + +svnchurn( + int commit : @svnentry ref, + int file : @file ref, + int addedLines : int ref, + int deletedLines : int ref +) + +/* + * C++ dbscheme + */ + +@location = @location_stmt | @location_expr | @location_default ; + +/** + * The location of an element that is not an expression or a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ +locations_default( + /** The location of an element that is not an expression or a statement. */ + unique int id: @location_default, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of a statement. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ +locations_stmt( + /** The location of a statement. */ + unique int id: @location_stmt, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** + * The location of an expression. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ +locations_expr( + /** The location of an expression. */ + unique int id: @location_expr, + int container: @container ref, + int startLine: int ref, + int startColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +/** An element for which line-count information is available. */ +@sourceline = @file | @function | @variable | @enumconstant | @xmllocatable; + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +files( + unique int id: @file, + string name: string ref +); + +folders( + unique int id: @folder, + string name: string ref +); + +@container = @folder | @file + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +fileannotations( + int id: @file ref, + int kind: int ref, + string name: string ref, + string value: string ref +); + +inmacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +affectedbymacroexpansion( + int id: @element ref, + int inv: @macroinvocation ref +); + +/* + case @macroinvocations.kind of + 1 = macro expansion + | 2 = other macro reference + ; +*/ +macroinvocations( + unique int id: @macroinvocation, + int macro_id: @ppd_define ref, + int location: @location_default ref, + int kind: int ref +); + +macroparent( + unique int id: @macroinvocation ref, + int parent_id: @macroinvocation ref +); + +// a macroinvocation may be part of another location +// the way to find a constant expression that uses a macro +// is thus to find a constant expression that has a location +// to which a macro invocation is bound +macrolocationbind( + int id: @macroinvocation ref, + int location: @location ref +); + +#keyset[invocation, argument_index] +macro_argument_unexpanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +#keyset[invocation, argument_index] +macro_argument_expanded( + int invocation: @macroinvocation ref, + int argument_index: int ref, + string text: string ref +); + +/* + case @function.kind of + 1 = normal + | 2 = constructor + | 3 = destructor + | 4 = conversion + | 5 = operator + | 6 = builtin // GCC built-in functions, e.g. __builtin___memcpy_chk + ; +*/ +functions( + unique int id: @function, + string name: string ref, + int kind: int ref +); + +function_entry_point(int id: @function ref, unique int entry_point: @stmt ref); + +function_return_type(int id: @function ref, int return_type: @type ref); + +/** If `function` is a coroutine, then this gives the + std::experimental::resumable_traits instance associated with it, + and the variables representing the `handle` and `promise` for it. */ +coroutine( + unique int function: @function ref, + int traits: @type ref, + int handle: @variable ref, + int promise: @variable ref +); + +/** The `new` function used for allocating the coroutine state, if any. */ +coroutine_new( + unique int function: @function ref, + int new: @function ref +); + +/** The `delete` function used for deallocating the coroutine state, if any. */ +coroutine_delete( + unique int function: @function ref, + int delete: @function ref +); + +purefunctions(unique int id: @function ref); + +function_deleted(unique int id: @function ref); + +function_defaulted(unique int id: @function ref); + +member_function_this_type(unique int id: @function ref, int this_type: @type ref); + +#keyset[id, type_id] +fun_decls( + int id: @fun_decl, + int function: @function ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +fun_def(unique int id: @fun_decl ref); +fun_specialized(unique int id: @fun_decl ref); +fun_implicit(unique int id: @fun_decl ref); +fun_decl_specifiers( + int id: @fun_decl ref, + string name: string ref +) +#keyset[fun_decl, index] +fun_decl_throws( + int fun_decl: @fun_decl ref, + int index: int ref, + int type_id: @type ref +); +/* an empty throw specification is different from none */ +fun_decl_empty_throws(unique int fun_decl: @fun_decl ref); +fun_decl_noexcept( + int fun_decl: @fun_decl ref, + int constant: @expr ref +); +fun_decl_empty_noexcept(int fun_decl: @fun_decl ref); +fun_decl_typedef_type( + unique int fun_decl: @fun_decl ref, + int typedeftype_id: @usertype ref +); + +param_decl_bind( + unique int id: @var_decl ref, + int index: int ref, + int fun_decl: @fun_decl ref +); + +#keyset[id, type_id] +var_decls( + int id: @var_decl, + int variable: @variable ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); +var_def(unique int id: @var_decl ref); +var_decl_specifiers( + int id: @var_decl ref, + string name: string ref +) +is_structured_binding(unique int id: @variable ref); + +type_decls( + unique int id: @type_decl, + int type_id: @type ref, + int location: @location_default ref +); +type_def(unique int id: @type_decl ref); +type_decl_top( + unique int type_decl: @type_decl ref +); + +namespace_decls( + unique int id: @namespace_decl, + int namespace_id: @namespace ref, + int location: @location_default ref, + int bodylocation: @location_default ref +); + +usings( + unique int id: @using, + int element_id: @element ref, + int location: @location_default ref +); + +/** The element which contains the `using` declaration. */ +using_container( + int parent: @element ref, + int child: @using ref +); + +static_asserts( + unique int id: @static_assert, + int condition : @expr ref, + string message : string ref, + int location: @location_default ref, + int enclosing : @element ref +); + +// each function has an ordered list of parameters +#keyset[id, type_id] +#keyset[function, index, type_id] +params( + int id: @parameter, + int function: @functionorblock ref, + int index: int ref, + int type_id: @type ref +); + +overrides(int new: @function ref, int old: @function ref); + +#keyset[id, type_id] +membervariables( + int id: @membervariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +globalvariables( + int id: @globalvariable, + int type_id: @type ref, + string name: string ref +); + +#keyset[id, type_id] +localvariables( + int id: @localvariable, + int type_id: @type ref, + string name: string ref +); + +autoderivation( + unique int var: @variable ref, + int derivation_type: @type ref +); + +enumconstants( + unique int id: @enumconstant, + int parent: @usertype ref, + int index: int ref, + int type_id: @type ref, + string name: string ref, + int location: @location_default ref +); + +@variable = @localscopevariable | @globalvariable | @membervariable; + +@localscopevariable = @localvariable | @parameter; + +/* + Built-in types are the fundamental types, e.g., integral, floating, and void. + + case @builtintype.kind of + 1 = error + | 2 = unknown + | 3 = void + | 4 = boolean + | 5 = char + | 6 = unsigned_char + | 7 = signed_char + | 8 = short + | 9 = unsigned_short + | 10 = signed_short + | 11 = int + | 12 = unsigned_int + | 13 = signed_int + | 14 = long + | 15 = unsigned_long + | 16 = signed_long + | 17 = long_long + | 18 = unsigned_long_long + | 19 = signed_long_long + | 20 = __int8 // Microsoft-specific + | 21 = __int16 // Microsoft-specific + | 22 = __int32 // Microsoft-specific + | 23 = __int64 // Microsoft-specific + | 24 = float + | 25 = double + | 26 = long_double + | 27 = _Complex_float // C99-specific + | 28 = _Complex_double // C99-specific + | 29 = _Complex_long double // C99-specific + | 30 = _Imaginary_float // C99-specific + | 31 = _Imaginary_double // C99-specific + | 32 = _Imaginary_long_double // C99-specific + | 33 = wchar_t // Microsoft-specific + | 34 = decltype_nullptr // C++11 + | 35 = __int128 + | 36 = unsigned___int128 + | 37 = signed___int128 + | 38 = __float128 + | 39 = _Complex___float128 + | 40 = _Decimal32 + | 41 = _Decimal64 + | 42 = _Decimal128 + | 43 = char16_t + | 44 = char32_t + | 45 = _Float32 + | 46 = _Float32x + | 47 = _Float64 + | 48 = _Float64x + | 49 = _Float128 + | 50 = _Float128x + | 51 = char8_t + ; +*/ +builtintypes( + unique int id: @builtintype, + string name: string ref, + int kind: int ref, + int size: int ref, + int sign: int ref, + int alignment: int ref +); + +/* + Derived types are types that are directly derived from existing types and + point to, refer to, transform type data to return a new type. + + case @derivedtype.kind of + 1 = pointer + | 2 = reference + | 3 = type_with_specifiers + | 4 = array + | 5 = gnu_vector + | 6 = routineptr + | 7 = routinereference + | 8 = rvalue_reference // C++11 +// ... 9 type_conforming_to_protocols deprecated + | 10 = block + ; +*/ +derivedtypes( + unique int id: @derivedtype, + string name: string ref, + int kind: int ref, + int type_id: @type ref +); + +pointerishsize(unique int id: @derivedtype ref, + int size: int ref, + int alignment: int ref); + +arraysizes( + unique int id: @derivedtype ref, + int num_elements: int ref, + int bytesize: int ref, + int alignment: int ref +); + +typedefbase( + unique int id: @usertype ref, + int type_id: @type ref +); + +/** + * An instance of the C++11 `decltype` operator. For example: + * ``` + * int a; + * decltype(1+a) b; + * ``` + * Here `expr` is `1+a`. + * + * Sometimes an additional pair of parentheses around the expression + * would change the semantics of this decltype, e.g. + * ``` + * struct A { double x; }; + * const A* a = new A(); + * decltype( a->x ); // type is double + * decltype((a->x)); // type is const double& + * ``` + * (Please consult the C++11 standard for more details). + * `parentheses_would_change_meaning` is `true` iff that is the case. + */ +#keyset[id, expr] +decltypes( + int id: @decltype, + int expr: @expr ref, + int base_type: @type ref, + boolean parentheses_would_change_meaning: boolean ref +); + +/* + case @usertype.kind of + 1 = struct + | 2 = class + | 3 = union + | 4 = enum + | 5 = typedef // classic C: typedef typedef type name + | 6 = template + | 7 = template_parameter + | 8 = template_template_parameter + | 9 = proxy_class // a proxy class associated with a template parameter +// ... 10 objc_class deprecated +// ... 11 objc_protocol deprecated +// ... 12 objc_category deprecated + | 13 = scoped_enum + | 14 = using_alias // a using name = type style typedef + ; +*/ +usertypes( + unique int id: @usertype, + string name: string ref, + int kind: int ref +); + +usertypesize( + unique int id: @usertype ref, + int size: int ref, + int alignment: int ref +); + +usertype_final(unique int id: @usertype ref); + +usertype_uuid( + unique int id: @usertype ref, + string uuid: string ref +); + +mangled_name( + unique int id: @declaration ref, + int mangled_name : @mangledname +); + +is_pod_class(unique int id: @usertype ref); +is_standard_layout_class(unique int id: @usertype ref); + +is_complete(unique int id: @usertype ref); + +is_class_template(unique int id: @usertype ref); +class_instantiation( + int to: @usertype ref, + int from: @usertype ref +); +class_template_argument( + int type_id: @usertype ref, + int index: int ref, + int arg_type: @type ref +); +class_template_argument_value( + int type_id: @usertype ref, + int index: int ref, + int arg_value: @expr ref +); + +is_proxy_class_for( + unique int id: @usertype ref, + unique int templ_param_id: @usertype ref +); + +type_mentions( + unique int id: @type_mention, + int type_id: @type ref, + int location: @location ref, + // a_symbol_reference_kind from the EDG frontend. See symbol_ref.h there. + int kind: int ref +); + +is_function_template(unique int id: @function ref); +function_instantiation( + unique int to: @function ref, + int from: @function ref +); +function_template_argument( + int function_id: @function ref, + int index: int ref, + int arg_type: @type ref +); +function_template_argument_value( + int function_id: @function ref, + int index: int ref, + int arg_value: @expr ref +); + +is_variable_template(unique int id: @variable ref); +variable_instantiation( + unique int to: @variable ref, + int from: @variable ref +); +variable_template_argument( + int variable_id: @variable ref, + int index: int ref, + int arg_type: @type ref +); +variable_template_argument_value( + int variable_id: @variable ref, + int index: int ref, + int arg_value: @expr ref +); + +/* + Fixed point types + precision(1) = short, precision(2) = default, precision(3) = long + is_unsigned(1) = unsigned is_unsigned(2) = signed + is_fract_type(1) = declared with _Fract + saturating(1) = declared with _Sat +*/ +/* TODO +fixedpointtypes( + unique int id: @fixedpointtype, + int precision: int ref, + int is_unsigned: int ref, + int is_fract_type: int ref, + int saturating: int ref); +*/ + +routinetypes( + unique int id: @routinetype, + int return_type: @type ref +); + +routinetypeargs( + int routine: @routinetype ref, + int index: int ref, + int type_id: @type ref +); + +ptrtomembers( + unique int id: @ptrtomember, + int type_id: @type ref, + int class_id: @type ref +); + +/* + specifiers for types, functions, and variables + + "public", + "protected", + "private", + + "const", + "volatile", + "static", + + "pure", + "virtual", + "sealed", // Microsoft + "__interface", // Microsoft + "inline", + "explicit", + + "near", // near far extension + "far", // near far extension + "__ptr32", // Microsoft + "__ptr64", // Microsoft + "__sptr", // Microsoft + "__uptr", // Microsoft + "dllimport", // Microsoft + "dllexport", // Microsoft + "thread", // Microsoft + "naked", // Microsoft + "microsoft_inline", // Microsoft + "forceinline", // Microsoft + "selectany", // Microsoft + "nothrow", // Microsoft + "novtable", // Microsoft + "noreturn", // Microsoft + "noinline", // Microsoft + "noalias", // Microsoft + "restrict", // Microsoft +*/ + +specifiers( + unique int id: @specifier, + unique string str: string ref +); + +typespecifiers( + int type_id: @type ref, + int spec_id: @specifier ref +); + +funspecifiers( + int func_id: @function ref, + int spec_id: @specifier ref +); + +varspecifiers( + int var_id: @accessible ref, + int spec_id: @specifier ref +); + +attributes( + unique int id: @attribute, + int kind: int ref, + string name: string ref, + string name_space: string ref, + int location: @location_default ref +); + +case @attribute.kind of + 0 = @gnuattribute +| 1 = @stdattribute +| 2 = @declspec +| 3 = @msattribute +| 4 = @alignas +// ... 5 @objc_propertyattribute deprecated +; + +attribute_args( + unique int id: @attribute_arg, + int kind: int ref, + int attribute: @attribute ref, + int index: int ref, + int location: @location_default ref +); + +case @attribute_arg.kind of + 0 = @attribute_arg_empty +| 1 = @attribute_arg_token +| 2 = @attribute_arg_constant +| 3 = @attribute_arg_type +; + +attribute_arg_value( + unique int arg: @attribute_arg ref, + string value: string ref +); +attribute_arg_type( + unique int arg: @attribute_arg ref, + int type_id: @type ref +); +attribute_arg_name( + unique int arg: @attribute_arg ref, + string name: string ref +); + +typeattributes( + int type_id: @type ref, + int spec_id: @attribute ref +); + +funcattributes( + int func_id: @function ref, + int spec_id: @attribute ref +); + +varattributes( + int var_id: @accessible ref, + int spec_id: @attribute ref +); + +stmtattributes( + int stmt_id: @stmt ref, + int spec_id: @attribute ref +); + +@type = @builtintype + | @derivedtype + | @usertype + /* TODO | @fixedpointtype */ + | @routinetype + | @ptrtomember + | @decltype; + +unspecifiedtype( + unique int type_id: @type ref, + int unspecified_type_id: @type ref +); + +member( + int parent: @type ref, + int index: int ref, + int child: @member ref +); + +@enclosingfunction_child = @usertype | @variable | @namespace + +enclosingfunction( + unique int child: @enclosingfunction_child ref, + int parent: @function ref +); + +derivations( + unique int derivation: @derivation, + int sub: @type ref, + int index: int ref, + int super: @type ref, + int location: @location_default ref +); + +derspecifiers( + int der_id: @derivation ref, + int spec_id: @specifier ref +); + +/** + * Contains the byte offset of the base class subobject within the derived + * class. Only holds for non-virtual base classes, but see table + * `virtual_base_offsets` for offsets of virtual base class subobjects. + */ +direct_base_offsets( + unique int der_id: @derivation ref, + int offset: int ref +); + +/** + * Contains the byte offset of the virtual base class subobject for class + * `super` within a most-derived object of class `sub`. `super` can be either a + * direct or indirect base class. + */ +#keyset[sub, super] +virtual_base_offsets( + int sub: @usertype ref, + int super: @usertype ref, + int offset: int ref +); + +frienddecls( + unique int id: @frienddecl, + int type_id: @type ref, + int decl_id: @declaration ref, + int location: @location_default ref +); + +@declaredtype = @usertype ; + +@declaration = @function + | @declaredtype + | @variable + | @enumconstant + | @frienddecl; + +@member = @membervariable + | @function + | @declaredtype + | @enumconstant; + +@locatable = @diagnostic + | @declaration + | @ppd_include + | @ppd_define + | @macroinvocation + /*| @funcall*/ + | @xmllocatable + | @attribute + | @attribute_arg; + +@namedscope = @namespace | @usertype; + +@element = @locatable + | @file + | @folder + | @specifier + | @type + | @expr + | @namespace + | @initialiser + | @stmt + | @derivation + | @comment + | @preprocdirect + | @fun_decl + | @var_decl + | @type_decl + | @namespace_decl + | @using + | @namequalifier + | @specialnamequalifyingelement + | @static_assert + | @type_mention + | @lambdacapture; + +@exprparent = @element; + +comments( + unique int id: @comment, + string contents: string ref, + int location: @location_default ref +); + +commentbinding( + int id: @comment ref, + int element: @element ref +); + +exprconv( + int converted: @expr ref, + unique int conversion: @expr ref +); + +compgenerated(unique int id: @element ref); + +/** + * `destructor_call` destructs the `i`'th entity that should be + * destructed following `element`. Note that entities should be + * destructed in reverse construction order, so for a given `element` + * these should be called from highest to lowest `i`. + */ +#keyset[element, destructor_call] +#keyset[element, i] +synthetic_destructor_call( + int element: @element ref, + int i: int ref, + int destructor_call: @routineexpr ref +); + +namespaces( + unique int id: @namespace, + string name: string ref +); + +namespace_inline( + unique int id: @namespace ref +); + +namespacembrs( + int parentid: @namespace ref, + unique int memberid: @namespacembr ref +); + +@namespacembr = @declaration | @namespace; + +exprparents( + int expr_id: @expr ref, + int child_index: int ref, + int parent_id: @exprparent ref +); + +expr_isload(unique int expr_id: @expr ref); + +@cast = @c_style_cast + | @const_cast + | @dynamic_cast + | @reinterpret_cast + | @static_cast + ; + +/* +case @conversion.kind of + 0 = @simple_conversion // a numeric conversion, qualification conversion, or a reinterpret_cast +| 1 = @bool_conversion // conversion to 'bool' +| 2 = @base_class_conversion // a derived-to-base conversion +| 3 = @derived_class_conversion // a base-to-derived conversion +| 4 = @pm_base_class_conversion // a derived-to-base conversion of a pointer to member +| 5 = @pm_derived_class_conversion // a base-to-derived conversion of a pointer to member +| 6 = @glvalue_adjust // an adjustment of the type of a glvalue +| 7 = @prvalue_adjust // an adjustment of the type of a prvalue +; +*/ +/** + * Describes the semantics represented by a cast expression. This is largely + * independent of the source syntax of the cast, so it is separate from the + * regular expression kind. + */ +conversionkinds( + unique int expr_id: @cast ref, + int kind: int ref +); + +@conversion = @cast + | @array_to_pointer + | @parexpr + | @reference_to + | @ref_indirect + | @temp_init + ; + +/* +case @funbindexpr.kind of + 0 = @normal_call // a normal call +| 1 = @virtual_call // a virtual call +| 2 = @adl_call // a call whose target is only found by ADL +; +*/ +iscall(unique int caller: @funbindexpr ref, int kind: int ref); + +numtemplatearguments( + unique int expr_id: @expr ref, + int num: int ref +); + +specialnamequalifyingelements( + unique int id: @specialnamequalifyingelement, + unique string name: string ref +); + +@namequalifiableelement = @expr | @namequalifier; +@namequalifyingelement = @namespace + | @specialnamequalifyingelement + | @usertype; + +namequalifiers( + unique int id: @namequalifier, + unique int qualifiableelement: @namequalifiableelement ref, + int qualifyingelement: @namequalifyingelement ref, + int location: @location_default ref +); + +varbind( + int expr: @varbindexpr ref, + int var: @accessible ref +); + +funbind( + int expr: @funbindexpr ref, + int fun: @function ref +); + +@any_new_expr = @new_expr + | @new_array_expr; + +@new_or_delete_expr = @any_new_expr + | @delete_expr + | @delete_array_expr; + +@prefix_crement_expr = @preincrexpr | @predecrexpr; + +@postfix_crement_expr = @postincrexpr | @postdecrexpr; + +@increment_expr = @preincrexpr | @postincrexpr; + +@decrement_expr = @predecrexpr | @postdecrexpr; + +@crement_expr = @increment_expr | @decrement_expr; + +@un_arith_op_expr = @arithnegexpr + | @unaryplusexpr + | @conjugation + | @realpartexpr + | @imagpartexpr + | @crement_expr + ; + +@un_bitwise_op_expr = @complementexpr; + +@un_log_op_expr = @notexpr; + +@un_op_expr = @address_of + | @indirect + | @un_arith_op_expr + | @un_bitwise_op_expr + | @builtinaddressof + | @vec_fill + | @un_log_op_expr + | @co_await + | @co_yield + ; + +@bin_log_op_expr = @andlogicalexpr | @orlogicalexpr; + +@cmp_op_expr = @eq_op_expr | @rel_op_expr; + +@eq_op_expr = @eqexpr | @neexpr; + +@rel_op_expr = @gtexpr + | @ltexpr + | @geexpr + | @leexpr + | @spaceshipexpr + ; + +@bin_bitwise_op_expr = @lshiftexpr + | @rshiftexpr + | @andexpr + | @orexpr + | @xorexpr + ; + +@p_arith_op_expr = @paddexpr + | @psubexpr + | @pdiffexpr + ; + +@bin_arith_op_expr = @addexpr + | @subexpr + | @mulexpr + | @divexpr + | @remexpr + | @jmulexpr + | @jdivexpr + | @fjaddexpr + | @jfaddexpr + | @fjsubexpr + | @jfsubexpr + | @minexpr + | @maxexpr + | @p_arith_op_expr + ; + +@bin_op_expr = @bin_arith_op_expr + | @bin_bitwise_op_expr + | @cmp_op_expr + | @bin_log_op_expr + ; + +@op_expr = @un_op_expr + | @bin_op_expr + | @assign_expr + | @conditionalexpr + ; + +@assign_arith_expr = @assignaddexpr + | @assignsubexpr + | @assignmulexpr + | @assigndivexpr + | @assignremexpr + ; + +@assign_bitwise_expr = @assignandexpr + | @assignorexpr + | @assignxorexpr + | @assignlshiftexpr + | @assignrshiftexpr + | @assignpaddexpr + | @assignpsubexpr + ; + +@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr + +@assign_expr = @assignexpr | @assign_op_expr + +/* + case @allocator.form of + 0 = plain + | 1 = alignment + ; +*/ + +/** + * The allocator function associated with a `new` or `new[]` expression. + * The `form` column specified whether the allocation call contains an alignment + * argument. + */ +expr_allocator( + unique int expr: @any_new_expr ref, + int func: @function ref, + int form: int ref +); + +/* + case @deallocator.form of + 0 = plain + | 1 = size + | 2 = alignment + | 3 = size_and_alignment + ; +*/ + +/** + * The deallocator function associated with a `delete`, `delete[]`, `new`, or + * `new[]` expression. For a `new` or `new[]` expression, the deallocator is the + * one used to free memory if the initialization throws an exception. + * The `form` column specifies whether the deallocation call contains a size + * argument, and alignment argument, or both. + */ +expr_deallocator( + unique int expr: @new_or_delete_expr ref, + int func: @function ref, + int form: int ref +); + +/** + * Holds if the `@conditionalexpr` is of the two operand form + * `guard ? : false`. + */ +expr_cond_two_operand( + unique int cond: @conditionalexpr ref +); + +/** + * The guard of `@conditionalexpr` `guard ? true : false` + */ +expr_cond_guard( + unique int cond: @conditionalexpr ref, + int guard: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` holds. For the two operand form + * `guard ?: false` consider using `expr_cond_guard` instead. + */ +expr_cond_true( + unique int cond: @conditionalexpr ref, + int true: @expr ref +); + +/** + * The expression used when the guard of `@conditionalexpr` + * `guard ? true : false` does not hold. + */ +expr_cond_false( + unique int cond: @conditionalexpr ref, + int false: @expr ref +); + +/** A string representation of the value. */ +values( + unique int id: @value, + string str: string ref +); + +/** The actual text in the source code for the value, if any. */ +valuetext( + unique int id: @value ref, + string text: string ref +); + +valuebind( + int val: @value ref, + unique int expr: @expr ref +); + +fieldoffsets( + unique int id: @variable ref, + int byteoffset: int ref, + int bitoffset: int ref +); + +bitfield( + unique int id: @variable ref, + int bits: int ref, + int declared_bits: int ref +); + +/* TODO +memberprefix( + int member: @expr ref, + int prefix: @expr ref +); +*/ + +/* + kind(1) = mbrcallexpr + kind(2) = mbrptrcallexpr + kind(3) = mbrptrmbrcallexpr + kind(4) = ptrmbrptrmbrcallexpr + kind(5) = mbrreadexpr // x.y + kind(6) = mbrptrreadexpr // p->y + kind(7) = mbrptrmbrreadexpr // x.*pm + kind(8) = mbrptrmbrptrreadexpr // x->*pm + kind(9) = staticmbrreadexpr // static x.y + kind(10) = staticmbrptrreadexpr // static p->y +*/ +/* TODO +memberaccess( + int member: @expr ref, + int kind: int ref +); +*/ + +initialisers( + unique int init: @initialiser, + int var: @accessible ref, + unique int expr: @expr ref, + int location: @location_expr ref +); + +braced_initialisers( + int init: @initialiser ref +); + +/** + * An ancestor for the expression, for cases in which we cannot + * otherwise find the expression's parent. + */ +expr_ancestor( + int exp: @expr ref, + int ancestor: @element ref +); + +exprs( + unique int id: @expr, + int kind: int ref, + int location: @location_expr ref +); + +/* + case @value.category of + 1 = prval + | 2 = xval + | 3 = lval + ; +*/ +expr_types( + int id: @expr ref, + int typeid: @type ref, + int value_category: int ref +); + +case @expr.kind of + 1 = @errorexpr +| 2 = @address_of // & AddressOfExpr +| 3 = @reference_to // ReferenceToExpr (implicit?) +| 4 = @indirect // * PointerDereferenceExpr +| 5 = @ref_indirect // ReferenceDereferenceExpr (implicit?) +// ... +| 8 = @array_to_pointer // (???) +| 9 = @vacuous_destructor_call // VacuousDestructorCall +// ... +| 11 = @assume // Microsoft +| 12 = @parexpr +| 13 = @arithnegexpr +| 14 = @unaryplusexpr +| 15 = @complementexpr +| 16 = @notexpr +| 17 = @conjugation // GNU ~ operator +| 18 = @realpartexpr // GNU __real +| 19 = @imagpartexpr // GNU __imag +| 20 = @postincrexpr +| 21 = @postdecrexpr +| 22 = @preincrexpr +| 23 = @predecrexpr +| 24 = @conditionalexpr +| 25 = @addexpr +| 26 = @subexpr +| 27 = @mulexpr +| 28 = @divexpr +| 29 = @remexpr +| 30 = @jmulexpr // C99 mul imaginary +| 31 = @jdivexpr // C99 div imaginary +| 32 = @fjaddexpr // C99 add real + imaginary +| 33 = @jfaddexpr // C99 add imaginary + real +| 34 = @fjsubexpr // C99 sub real - imaginary +| 35 = @jfsubexpr // C99 sub imaginary - real +| 36 = @paddexpr // pointer add (pointer + int or int + pointer) +| 37 = @psubexpr // pointer sub (pointer - integer) +| 38 = @pdiffexpr // difference between two pointers +| 39 = @lshiftexpr +| 40 = @rshiftexpr +| 41 = @andexpr +| 42 = @orexpr +| 43 = @xorexpr +| 44 = @eqexpr +| 45 = @neexpr +| 46 = @gtexpr +| 47 = @ltexpr +| 48 = @geexpr +| 49 = @leexpr +| 50 = @minexpr // GNU minimum +| 51 = @maxexpr // GNU maximum +| 52 = @assignexpr +| 53 = @assignaddexpr +| 54 = @assignsubexpr +| 55 = @assignmulexpr +| 56 = @assigndivexpr +| 57 = @assignremexpr +| 58 = @assignlshiftexpr +| 59 = @assignrshiftexpr +| 60 = @assignandexpr +| 61 = @assignorexpr +| 62 = @assignxorexpr +| 63 = @assignpaddexpr // assign pointer add +| 64 = @assignpsubexpr // assign pointer sub +| 65 = @andlogicalexpr +| 66 = @orlogicalexpr +| 67 = @commaexpr +| 68 = @subscriptexpr // access to member of an array, e.g., a[5] +// ... 69 @objc_subscriptexpr deprecated +// ... 70 @cmdaccess deprecated +// ... +| 73 = @virtfunptrexpr +| 74 = @callexpr +// ... 75 @msgexpr_normal deprecated +// ... 76 @msgexpr_super deprecated +// ... 77 @atselectorexpr deprecated +// ... 78 @atprotocolexpr deprecated +| 79 = @vastartexpr +| 80 = @vaargexpr +| 81 = @vaendexpr +| 82 = @vacopyexpr +// ... 83 @atencodeexpr deprecated +| 84 = @varaccess +| 85 = @thisaccess +// ... 86 @objc_box_expr deprecated +| 87 = @new_expr +| 88 = @delete_expr +| 89 = @throw_expr +| 90 = @condition_decl // a variable declared in a condition, e.g., if(int x = y > 2) +| 91 = @braced_init_list +| 92 = @type_id +| 93 = @runtime_sizeof +| 94 = @runtime_alignof +| 95 = @sizeof_pack +| 96 = @expr_stmt // GNU extension +| 97 = @routineexpr +| 98 = @type_operand // used to access a type in certain contexts (haven't found any examples yet....) +| 99 = @offsetofexpr // offsetof ::= type and field +| 100 = @hasassignexpr // __has_assign ::= type +| 101 = @hascopyexpr // __has_copy ::= type +| 102 = @hasnothrowassign // __has_nothrow_assign ::= type +| 103 = @hasnothrowconstr // __has_nothrow_constructor ::= type +| 104 = @hasnothrowcopy // __has_nothrow_copy ::= type +| 105 = @hastrivialassign // __has_trivial_assign ::= type +| 106 = @hastrivialconstr // __has_trivial_constructor ::= type +| 107 = @hastrivialcopy // __has_trivial_copy ::= type +| 108 = @hasuserdestr // __has_user_destructor ::= type +| 109 = @hasvirtualdestr // __has_virtual_destructor ::= type +| 110 = @isabstractexpr // __is_abstract ::= type +| 111 = @isbaseofexpr // __is_base_of ::= type type +| 112 = @isclassexpr // __is_class ::= type +| 113 = @isconvtoexpr // __is_convertible_to ::= type type +| 114 = @isemptyexpr // __is_empty ::= type +| 115 = @isenumexpr // __is_enum ::= type +| 116 = @ispodexpr // __is_pod ::= type +| 117 = @ispolyexpr // __is_polymorphic ::= type +| 118 = @isunionexpr // __is_union ::= type +| 119 = @typescompexpr // GNU __builtin_types_compatible ::= type type +| 120 = @intaddrexpr // EDG internal builtin, used to implement offsetof +// ... +| 122 = @hastrivialdestructor // __has_trivial_destructor ::= type +| 123 = @literal +| 124 = @uuidof +| 127 = @aggregateliteral +| 128 = @delete_array_expr +| 129 = @new_array_expr +// ... 130 @objc_array_literal deprecated +// ... 131 @objc_dictionary_literal deprecated +| 132 = @foldexpr +// ... +| 200 = @ctordirectinit +| 201 = @ctorvirtualinit +| 202 = @ctorfieldinit +| 203 = @ctordelegatinginit +| 204 = @dtordirectdestruct +| 205 = @dtorvirtualdestruct +| 206 = @dtorfielddestruct +// ... +| 210 = @static_cast +| 211 = @reinterpret_cast +| 212 = @const_cast +| 213 = @dynamic_cast +| 214 = @c_style_cast +| 215 = @lambdaexpr +| 216 = @param_ref +| 217 = @noopexpr +// ... +| 294 = @istriviallyconstructibleexpr +| 295 = @isdestructibleexpr +| 296 = @isnothrowdestructibleexpr +| 297 = @istriviallydestructibleexpr +| 298 = @istriviallyassignableexpr +| 299 = @isnothrowassignableexpr +| 300 = @istrivialexpr +| 301 = @isstandardlayoutexpr +| 302 = @istriviallycopyableexpr +| 303 = @isliteraltypeexpr +| 304 = @hastrivialmoveconstructorexpr +| 305 = @hastrivialmoveassignexpr +| 306 = @hasnothrowmoveassignexpr +| 307 = @isconstructibleexpr +| 308 = @isnothrowconstructibleexpr +| 309 = @hasfinalizerexpr +| 310 = @isdelegateexpr +| 311 = @isinterfaceclassexpr +| 312 = @isrefarrayexpr +| 313 = @isrefclassexpr +| 314 = @issealedexpr +| 315 = @issimplevalueclassexpr +| 316 = @isvalueclassexpr +| 317 = @isfinalexpr +| 319 = @noexceptexpr +| 320 = @builtinshufflevector +| 321 = @builtinchooseexpr +| 322 = @builtinaddressof +| 323 = @vec_fill +| 324 = @builtinconvertvector +| 325 = @builtincomplex +| 326 = @spaceshipexpr +| 327 = @co_await +| 328 = @co_yield +| 329 = @temp_init +| 330 = @isassignable +| 331 = @isaggregate +| 332 = @hasuniqueobjectrepresentations +| 333 = @builtinbitcast +| 334 = @builtinshuffle +; + +@var_args_expr = @vastartexpr + | @vaendexpr + | @vaargexpr + | @vacopyexpr + ; + +@builtin_op = @var_args_expr + | @noopexpr + | @offsetofexpr + | @intaddrexpr + | @hasassignexpr + | @hascopyexpr + | @hasnothrowassign + | @hasnothrowconstr + | @hasnothrowcopy + | @hastrivialassign + | @hastrivialconstr + | @hastrivialcopy + | @hastrivialdestructor + | @hasuserdestr + | @hasvirtualdestr + | @isabstractexpr + | @isbaseofexpr + | @isclassexpr + | @isconvtoexpr + | @isemptyexpr + | @isenumexpr + | @ispodexpr + | @ispolyexpr + | @isunionexpr + | @typescompexpr + | @builtinshufflevector + | @builtinconvertvector + | @builtinaddressof + | @istriviallyconstructibleexpr + | @isdestructibleexpr + | @isnothrowdestructibleexpr + | @istriviallydestructibleexpr + | @istriviallyassignableexpr + | @isnothrowassignableexpr + | @isstandardlayoutexpr + | @istriviallycopyableexpr + | @isliteraltypeexpr + | @hastrivialmoveconstructorexpr + | @hastrivialmoveassignexpr + | @hasnothrowmoveassignexpr + | @isconstructibleexpr + | @isnothrowconstructibleexpr + | @hasfinalizerexpr + | @isdelegateexpr + | @isinterfaceclassexpr + | @isrefarrayexpr + | @isrefclassexpr + | @issealedexpr + | @issimplevalueclassexpr + | @isvalueclassexpr + | @isfinalexpr + | @builtinchooseexpr + | @builtincomplex + | @isassignable + | @isaggregate + | @hasuniqueobjectrepresentations + | @builtinbitcast + | @builtinshuffle + ; + +new_allocated_type( + unique int expr: @new_expr ref, + int type_id: @type ref +); + +new_array_allocated_type( + unique int expr: @new_array_expr ref, + int type_id: @type ref +); + +/** + * The field being initialized by an initializer expression within an aggregate + * initializer for a class/struct/union. + */ +#keyset[aggregate, field] +aggregate_field_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int field: @membervariable ref +); + +/** + * The index of the element being initialized by an initializer expression + * within an aggregate initializer for an array. + */ +#keyset[aggregate, element_index] +aggregate_array_init( + int aggregate: @aggregateliteral ref, + int initializer: @expr ref, + int element_index: int ref +); + +@ctorinit = @ctordirectinit + | @ctorvirtualinit + | @ctorfieldinit + | @ctordelegatinginit; +@dtordestruct = @dtordirectdestruct + | @dtorvirtualdestruct + | @dtorfielddestruct; + + +condition_decl_bind( + unique int expr: @condition_decl ref, + unique int decl: @declaration ref +); + +typeid_bind( + unique int expr: @type_id ref, + int type_id: @type ref +); + +uuidof_bind( + unique int expr: @uuidof ref, + int type_id: @type ref +); + +@runtime_sizeof_or_alignof = @runtime_sizeof | @runtime_alignof; + +sizeof_bind( + unique int expr: @runtime_sizeof_or_alignof ref, + int type_id: @type ref +); + +code_block( + unique int block: @literal ref, + unique int routine: @function ref +); + +lambdas( + unique int expr: @lambdaexpr ref, + string default_capture: string ref, + boolean has_explicit_return_type: boolean ref +); + +lambda_capture( + unique int id: @lambdacapture, + int lambda: @lambdaexpr ref, + int index: int ref, + int field: @membervariable ref, + boolean captured_by_reference: boolean ref, + boolean is_implicit: boolean ref, + int location: @location_default ref +); + +@funbindexpr = @routineexpr + | @new_expr + | @delete_expr + | @delete_array_expr + | @ctordirectinit + | @ctorvirtualinit + | @ctordelegatinginit + | @dtordirectdestruct + | @dtorvirtualdestruct; + +@varbindexpr = @varaccess | @ctorfieldinit | @dtorfielddestruct; +@addressable = @function | @variable ; +@accessible = @addressable | @enumconstant ; + +@access = @varaccess | @routineexpr ; + +fold( + int expr: @foldexpr ref, + string operator: string ref, + boolean is_left_fold: boolean ref +); + +stmts( + unique int id: @stmt, + int kind: int ref, + int location: @location_stmt ref +); + +case @stmt.kind of + 1 = @stmt_expr +| 2 = @stmt_if +| 3 = @stmt_while +| 4 = @stmt_goto +| 5 = @stmt_label +| 6 = @stmt_return +| 7 = @stmt_block +| 8 = @stmt_end_test_while // do { ... } while ( ... ) +| 9 = @stmt_for +| 10 = @stmt_switch_case +| 11 = @stmt_switch +| 13 = @stmt_asm // "asm" statement or the body of an asm function +| 15 = @stmt_try_block +| 16 = @stmt_microsoft_try // Microsoft +| 17 = @stmt_decl +| 18 = @stmt_set_vla_size // C99 +| 19 = @stmt_vla_decl // C99 +| 25 = @stmt_assigned_goto // GNU +| 26 = @stmt_empty +| 27 = @stmt_continue +| 28 = @stmt_break +| 29 = @stmt_range_based_for // C++11 +// ... 30 @stmt_at_autoreleasepool_block deprecated +// ... 31 @stmt_objc_for_in deprecated +// ... 32 @stmt_at_synchronized deprecated +| 33 = @stmt_handler +// ... 34 @stmt_finally_end deprecated +| 35 = @stmt_constexpr_if +| 37 = @stmt_co_return +; + +type_vla( + int type_id: @type ref, + int decl: @stmt_vla_decl ref +); + +variable_vla( + int var: @variable ref, + int decl: @stmt_vla_decl ref +); + +if_initialization( + unique int if_stmt: @stmt_if ref, + int init_id: @stmt ref +); + +if_then( + unique int if_stmt: @stmt_if ref, + int then_id: @stmt ref +); + +if_else( + unique int if_stmt: @stmt_if ref, + int else_id: @stmt ref +); + +constexpr_if_initialization( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int init_id: @stmt ref +); + +constexpr_if_then( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int then_id: @stmt ref +); + +constexpr_if_else( + unique int constexpr_if_stmt: @stmt_constexpr_if ref, + int else_id: @stmt ref +); + +while_body( + unique int while_stmt: @stmt_while ref, + int body_id: @stmt ref +); + +do_body( + unique int do_stmt: @stmt_end_test_while ref, + int body_id: @stmt ref +); + +switch_initialization( + unique int switch_stmt: @stmt_switch ref, + int init_id: @stmt ref +); + +#keyset[switch_stmt, index] +switch_case( + int switch_stmt: @stmt_switch ref, + int index: int ref, + int case_id: @stmt_switch_case ref +); + +switch_body( + unique int switch_stmt: @stmt_switch ref, + int body_id: @stmt ref +); + +for_initialization( + unique int for_stmt: @stmt_for ref, + int init_id: @stmt ref +); + +for_condition( + unique int for_stmt: @stmt_for ref, + int condition_id: @expr ref +); + +for_update( + unique int for_stmt: @stmt_for ref, + int update_id: @expr ref +); + +for_body( + unique int for_stmt: @stmt_for ref, + int body_id: @stmt ref +); + +@stmtparent = @stmt | @expr_stmt ; +stmtparents( + unique int id: @stmt ref, + int index: int ref, + int parent: @stmtparent ref +); + +ishandler(unique int block: @stmt_block ref); + +@cfgnode = @stmt | @expr | @function | @initialiser ; + +stmt_decl_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl: @declaration ref +); + +stmt_decl_entry_bind( + int stmt: @stmt_decl ref, + int num: int ref, + int decl_entry: @element ref +); + +@functionorblock = @function | @stmt_block; + +blockscope( + unique int block: @stmt_block ref, + int enclosing: @functionorblock ref +); + +@jump = @stmt_goto | @stmt_break | @stmt_continue; + +@jumporlabel = @jump | @stmt_label | @literal; + +jumpinfo( + unique int id: @jumporlabel ref, + string str: string ref, + int target: @stmt ref +); + +preprocdirects( + unique int id: @preprocdirect, + int kind: int ref, + int location: @location_default ref +); +case @preprocdirect.kind of + 0 = @ppd_if +| 1 = @ppd_ifdef +| 2 = @ppd_ifndef +| 3 = @ppd_elif +| 4 = @ppd_else +| 5 = @ppd_endif +| 6 = @ppd_plain_include +| 7 = @ppd_define +| 8 = @ppd_undef +| 9 = @ppd_line +| 10 = @ppd_error +| 11 = @ppd_pragma +| 12 = @ppd_objc_import +| 13 = @ppd_include_next +| 18 = @ppd_warning +; + +@ppd_include = @ppd_plain_include | @ppd_objc_import | @ppd_include_next; + +@ppd_branch = @ppd_if | @ppd_ifdef | @ppd_ifndef | @ppd_elif; + +preprocpair( + int begin : @ppd_branch ref, + int elseelifend : @preprocdirect ref +); + +preproctrue(int branch : @ppd_branch ref); +preprocfalse(int branch : @ppd_branch ref); + +preproctext( + unique int id: @preprocdirect ref, + string head: string ref, + string body: string ref +); + +includes( + unique int id: @ppd_include ref, + int included: @file ref +); + +link_targets( + unique int id: @link_target, + int binary: @file ref +); + +link_parent( + int element : @element ref, + int link_target : @link_target ref +); + +/* XML Files */ + +xmlEncoding(unique int id: @file ref, string encoding: string ref); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters + | @xmlelement + | @xmlcomment + | @xmlattribute + | @xmldtd + | @file + | @xmlnamespace; diff --git a/cpp/ql/lib/upgrades/19e31bf071f588bb7efd1e4d5a185ce4f6fbbd84/upgrade.properties b/cpp/ql/lib/upgrades/19e31bf071f588bb7efd1e4d5a185ce4f6fbbd84/upgrade.properties new file mode 100644 index 00000000000..db0e7e92d0e --- /dev/null +++ b/cpp/ql/lib/upgrades/19e31bf071f588bb7efd1e4d5a185ce4f6fbbd84/upgrade.properties @@ -0,0 +1,2 @@ +description: Add new builtin operations +compatibility: backwards From afdd21eab7acbac11975ec20c5f09b4ed041ca05 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Tue, 26 Jul 2022 20:52:36 +0200 Subject: [PATCH 368/505] C++: Update DB scheme stats file --- cpp/ql/lib/semmlecode.cpp.dbscheme.stats | 5604 +++++++++++----------- 1 file changed, 2707 insertions(+), 2897 deletions(-) diff --git a/cpp/ql/lib/semmlecode.cpp.dbscheme.stats b/cpp/ql/lib/semmlecode.cpp.dbscheme.stats index e439c5ac624..35eb9c7e6de 100644 --- a/cpp/ql/lib/semmlecode.cpp.dbscheme.stats +++ b/cpp/ql/lib/semmlecode.cpp.dbscheme.stats @@ -2,79 +2,79 @@ @compilation - 9980 + 9978 @externalDataElement 65 - - @svnentry - 575525 - @external_package 4 + + @svnentry + 575525 + @location_stmt - 3805462 + 3805465 @diagnostic - 582773 + 72312 @file - 124189 + 124196 @folder 15994 - @location_expr - 13128112 + @location_default + 30132211 - @location_default - 30128761 + @location_expr + 13128120 @macroinvocation - 33894981 + 33889080 @function - 4726273 + 4726519 @fun_decl - 5096962 + 5097227 @type_decl - 3283977 + 3284148 @namespace_decl - 306972 + 306973 @using - 374921 + 374941 @static_assert - 130417 + 130418 @var_decl - 8543232 + 8543677 @parameter - 6660155 + 6660502 @membervariable @@ -82,7 +82,7 @@ @globalvariable - 300708 + 318724 @localvariable @@ -94,55 +94,55 @@ @builtintype - 22109 + 22110 @derivedtype - 4413446 + 4413676 @decltype - 31047 + 31049 @usertype - 5342989 + 5343268 @mangledname - 1728010 + 1975901 @type_mention - 4011508 + 4011511 @routinetype - 546982 + 546661 @ptrtomember - 38103 + 38105 @specifier - 24932 + 24933 @gnuattribute - 664228 + 664262 @stdattribute - 468982 + 469081 @alignas - 8937 + 8938 @declspec - 238544 + 238545 @msattribute @@ -150,11 +150,11 @@ @attribute_arg_token - 25402 + 25403 @attribute_arg_constant - 326469 + 326486 @attribute_arg_type @@ -166,15 +166,15 @@ @derivation - 402388 + 402152 @frienddecl - 715075 + 714656 @comment - 8781270 + 8783134 @namespace @@ -186,23 +186,23 @@ @namequalifier - 1618961 + 1618866 @value - 10646146 + 10646153 @initialiser - 1731824 + 1732353 @lambdacapture - 28224 + 28226 @stmt_expr - 1480414 + 1480415 @stmt_if @@ -210,23 +210,23 @@ @stmt_while - 30207 + 30201 @stmt_label - 53046 + 53044 @stmt_return - 1306346 + 1306414 @stmt_block - 1446530 + 1446605 @stmt_end_test_while - 148604 + 148599 @stmt_for @@ -234,7 +234,7 @@ @stmt_switch_case - 191408 + 191399 @stmt_switch @@ -242,11 +242,11 @@ @stmt_try_block - 42701 + 42699 @stmt_decl - 608508 + 608387 @stmt_empty @@ -258,7 +258,7 @@ @stmt_break - 102231 + 102232 @stmt_range_based_for @@ -266,15 +266,15 @@ @stmt_handler - 59432 + 59429 @stmt_constexpr_if - 52551 + 52562 @stmt_goto - 110490 + 110487 @stmt_asm @@ -310,43 +310,43 @@ @ctordirectinit - 112813 + 112747 @ctorvirtualinit - 6534 + 6532 @ctorfieldinit - 200789 + 200671 @ctordelegatinginit - 3347 + 3345 @dtordirectdestruct - 41715 + 41690 @dtorvirtualdestruct - 4122 + 4119 @dtorfielddestruct - 41644 + 41620 @static_cast - 211821 + 211675 @reinterpret_cast - 30776 + 30783 @const_cast - 35278 + 35285 @dynamic_cast @@ -354,19 +354,19 @@ @c_style_cast - 4209393 + 4209396 @lambdaexpr - 21639 + 21640 @param_ref - 375289 + 375271 @errorexpr - 46823 + 46796 @address_of @@ -374,27 +374,27 @@ @reference_to - 1596143 + 1596067 @indirect - 292115 + 292106 @ref_indirect - 1934537 + 1938685 @array_to_pointer - 1424883 + 1424884 @vacuous_destructor_call - 8138 + 8133 @parexpr - 3572547 + 3572549 @arithnegexpr @@ -402,11 +402,11 @@ @complementexpr - 27787 + 27786 @notexpr - 277992 + 277993 @postincrexpr @@ -446,11 +446,11 @@ @remexpr - 15819 + 15810 @paddexpr - 86505 + 86502 @psubexpr @@ -458,7 +458,7 @@ @pdiffexpr - 35487 + 35495 @lshiftexpr @@ -490,11 +490,11 @@ @gtexpr - 100669 + 100674 @ltexpr - 106314 + 106319 @geexpr @@ -502,11 +502,11 @@ @leexpr - 212141 + 212134 @assignexpr - 933419 + 933420 @assignaddexpr @@ -518,7 +518,7 @@ @assignmulexpr - 7170 + 7168 @assigndivexpr @@ -546,7 +546,7 @@ @assignxorexpr - 21804 + 21803 @assignpaddexpr @@ -554,7 +554,7 @@ @andlogicalexpr - 249008 + 249009 @orlogicalexpr @@ -562,7 +562,7 @@ @commaexpr - 181539 + 181530 @subscriptexpr @@ -570,7 +570,7 @@ @callexpr - 309063 + 309079 @vastartexpr @@ -586,27 +586,27 @@ @varaccess - 6006364 + 6006368 @thisaccess - 1125914 + 1125919 @new_expr - 47598 + 47571 @delete_expr - 11732 + 11725 @throw_expr - 21765 + 21760 @condition_decl - 38595 + 38593 @braced_init_list @@ -614,7 +614,7 @@ @type_id - 36430 + 36408 @runtime_sizeof @@ -622,15 +622,15 @@ @runtime_alignof - 48550 + 48521 @sizeof_pack - 5644 + 5645 @routineexpr - 3047613 + 3047738 @type_operand @@ -642,7 +642,7 @@ @ispodexpr - 636 + 635 @hastrivialdestructor @@ -666,23 +666,23 @@ @isconstructibleexpr - 2721 + 2722 @isfinalexpr - 2093 + 2094 @noexceptexpr - 23574 + 23573 @builtinaddressof - 13282 + 13274 @temp_init - 760683 + 760646 @assume @@ -826,7 +826,7 @@ @typescompexpr - 562415 + 562416 @intaddrexpr @@ -846,7 +846,7 @@ @istriviallyconstructibleexpr - 2826 + 2827 @isdestructibleexpr @@ -866,7 +866,7 @@ @isnothrowassignableexpr - 2093 + 2094 @isstandardlayoutexpr @@ -890,7 +890,7 @@ @isnothrowconstructibleexpr - 4815 + 4816 @hasfinalizerexpr @@ -956,45 +956,65 @@ @co_yield 1 + + @isassignable + 2617 + + + @isaggregate + 2 + + + @hasuniqueobjectrepresentations + 2 + + + @builtinbitcast + 1 + + + @builtinshuffle + 1965 + @ppd_if - 672225 + 672260 @ppd_ifdef - 265314 + 265328 @ppd_ifndef - 268607 + 268621 @ppd_elif - 25402 + 25403 @ppd_else - 210746 + 210757 @ppd_endif - 1206147 + 1206210 @ppd_plain_include - 313767 + 313784 @ppd_define - 2435252 + 2435769 @ppd_undef - 262021 + 262035 @ppd_pragma - 312270 + 312337 @ppd_include_next @@ -1048,11 +1068,11 @@ compilations - 9980 + 9978 id - 9980 + 9978 cwd @@ -1070,7 +1090,7 @@ 1 2 - 9980 + 9978 @@ -1644,7 +1664,7 @@ seconds - 12490 + 9586 @@ -1725,48 +1745,48 @@ 3 4 - 198 + 715 4 5 - 795 + 278 - 5 - 9 + 6 + 8 159 - 9 + 8 + 10 + 159 + + + 10 11 - 119 + 79 11 12 - 198 - - - 13 - 18 159 - 18 - 22 + 17 + 19 + 159 + + + 20 + 27 + 159 + + + 40 + 77 119 - - 22 - 46 - 159 - - - 60 - 104 - 79 - @@ -1833,12 +1853,12 @@ 3 4 - 556 + 835 4 5 - 1233 + 914 5 @@ -1848,32 +1868,27 @@ 6 7 - 119 + 437 7 8 - 318 + 79 8 9 - 119 + 278 9 - 11 + 26 278 - 11 - 31 - 278 - - - 40 - 95 - 159 + 26 + 92 + 238 @@ -1919,18 +1934,23 @@ 12 - 4 - 5 - 79 - - - 152 - 153 + 3 + 4 39 - 160 - 161 + 4 + 5 + 39 + + + 112 + 113 + 39 + + + 129 + 130 39 @@ -1947,22 +1967,27 @@ 1 2 - 7875 + 5449 2 3 - 2704 + 1670 3 4 - 1312 + 1272 4 - 45 - 596 + 5 + 676 + + + 5 + 42 + 517 @@ -1978,27 +2003,32 @@ 1 2 - 6682 + 5091 2 3 - 3142 + 1392 3 4 - 1431 - - - 4 - 6 954 - 6 - 65 - 278 + 4 + 5 + 875 + + + 5 + 7 + 795 + + + 7 + 67 + 477 @@ -2014,12 +2044,12 @@ 1 2 - 12251 + 9307 2 3 - 238 + 278 @@ -2029,23 +2059,23 @@ diagnostic_for - 1031027 + 891808 diagnostic - 582773 + 72312 compilation - 1988 + 9585 file_number - 104 + 11 file_number_diagnostic_number - 104788 + 6856 @@ -2059,27 +2089,17 @@ 1 2 - 362937 + 9631 2 3 - 92540 + 59836 - 3 - 4 - 70033 - - - 4 - 6 - 47421 - - - 6 - 9 - 9840 + 254 + 825 + 2844 @@ -2095,7 +2115,7 @@ 1 2 - 582773 + 72312 @@ -2111,22 +2131,7 @@ 1 2 - 475263 - - - 2 - 3 - 50143 - - - 3 - 6 - 53702 - - - 6 - 7 - 3663 + 72312 @@ -2135,222 +2140,6 @@ compilation diagnostic - - - 12 - - - 32 - 33 - 104 - - - 37 - 38 - 104 - - - 77 - 78 - 104 - - - 78 - 79 - 104 - - - 155 - 156 - 104 - - - 359 - 360 - 104 - - - 418 - 419 - 104 - - - 436 - 437 - 104 - - - 509 - 510 - 104 - - - 571 - 572 - 104 - - - 639 - 640 - 104 - - - 756 - 757 - 628 - - - 1001 - 1002 - 209 - - - - - - - compilation - file_number - - - 12 - - - 1 - 2 - 1988 - - - - - - - compilation - file_number_diagnostic_number - - - 12 - - - 32 - 33 - 104 - - - 37 - 38 - 104 - - - 77 - 78 - 104 - - - 78 - 79 - 104 - - - 155 - 156 - 104 - - - 359 - 360 - 104 - - - 418 - 419 - 104 - - - 436 - 437 - 104 - - - 509 - 510 - 104 - - - 571 - 572 - 104 - - - 639 - 640 - 104 - - - 756 - 757 - 628 - - - 1001 - 1002 - 209 - - - - - - - file_number - diagnostic - - - 12 - - - 5567 - 5568 - 104 - - - - - - - file_number - compilation - - - 12 - - - 19 - 20 - 104 - - - - - - - file_number - file_number_diagnostic_number - - - 12 - - - 1001 - 1002 - 104 - - - - - - - file_number_diagnostic_number - diagnostic 12 @@ -2358,37 +2147,193 @@ 2 3 - 25647 + 57 7 8 - 10991 + 6093 8 9 - 13085 + 497 - 9 - 10 - 13608 + 247 + 248 + 1965 - 10 - 11 - 27741 + 263 + 444 + 763 - 11 - 12 - 5652 + 446 + 594 + 208 + + + + + + + compilation + file_number + + + 12 + + + 1 + 2 + 9585 + + + + + + + compilation + file_number_diagnostic_number + + + 12 + + + 2 + 3 + 57 - 12 - 15 - 8060 + 7 + 8 + 6093 + + + 8 + 9 + 497 + + + 247 + 248 + 1965 + + + 263 + 444 + 763 + + + 446 + 594 + 208 + + + + + + + file_number + diagnostic + + + 12 + + + 6254 + 6255 + 11 + + + + + + + file_number + compilation + + + 12 + + + 829 + 830 + 11 + + + + + + + file_number + file_number_diagnostic_number + + + 12 + + + 593 + 594 + 11 + + + + + + + file_number_diagnostic_number + diagnostic + + + 12 + + + 1 + 2 + 2821 + + + 2 + 5 + 601 + + + 5 + 6 + 1017 + + + 7 + 14 + 543 + + + 15 + 16 + 57 + + + 17 + 18 + 601 + + + 18 + 23 + 462 + + + 26 + 40 + 555 + + + 42 + 830 + 196 @@ -2402,49 +2347,54 @@ 12 - 2 - 3 - 25647 - - - 8 + 4 9 - 12247 - - - 9 - 10 - 7118 + 589 10 11 - 6490 - - - 11 - 13 - 9526 - - - 13 - 14 - 6176 + 1005 14 - 15 - 21355 + 27 + 543 - 15 - 16 - 8060 + 30 + 31 + 57 - 16 - 20 - 8165 + 34 + 35 + 601 + + + 36 + 45 + 462 + + + 52 + 79 + 555 + + + 84 + 85 + 185 + + + 254 + 255 + 2763 + + + 297 + 830 + 92 @@ -2460,7 +2410,7 @@ 1 2 - 104788 + 6856 @@ -2470,19 +2420,19 @@ compilation_finished - 9980 + 9978 id - 9980 + 9978 cpu_seconds - 7956 + 7850 elapsed_seconds - 138 + 161 @@ -2496,7 +2446,7 @@ 1 2 - 9980 + 9978 @@ -2512,7 +2462,7 @@ 1 2 - 9980 + 9978 @@ -2528,17 +2478,17 @@ 1 2 - 6777 + 6498 2 3 - 786 + 1005 3 - 17 - 393 + 15 + 346 @@ -2554,12 +2504,12 @@ 1 2 - 7505 + 7423 2 3 - 451 + 427 @@ -2572,10 +2522,15 @@ 12 + + 1 + 2 + 23 + 2 3 - 23 + 11 3 @@ -2583,43 +2538,43 @@ 23 - 9 - 10 + 8 + 9 + 23 + + + 11 + 12 11 - 14 - 15 + 24 + 25 11 - 22 - 23 + 49 + 50 11 - 37 - 38 + 104 + 105 11 - 144 - 145 + 155 + 156 11 - 162 - 163 + 239 + 240 11 - 222 - 223 - 11 - - - 243 - 244 + 255 + 256 11 @@ -2633,10 +2588,15 @@ 12 + + 1 + 2 + 23 + 2 3 - 23 + 11 3 @@ -2644,43 +2604,43 @@ 23 - 9 - 10 + 8 + 9 + 23 + + + 11 + 12 11 - 14 - 15 + 24 + 25 11 - 22 - 23 + 49 + 50 11 - 36 - 37 + 104 + 105 11 - 118 - 119 + 111 + 112 11 - 123 - 124 + 174 + 175 11 - 177 - 178 - 11 - - - 218 - 219 + 217 + 218 11 @@ -4405,31 +4365,31 @@ locations_default - 30128761 + 30132211 id - 30128761 + 30132211 container - 140184 + 140191 startLine - 2114992 + 2115102 startColumn - 37162 + 37164 endLine - 2117814 + 2117925 endColumn - 48452 + 48455 @@ -4443,7 +4403,7 @@ 1 2 - 30128761 + 30132211 @@ -4459,7 +4419,7 @@ 1 2 - 30128761 + 30132211 @@ -4475,7 +4435,7 @@ 1 2 - 30128761 + 30132211 @@ -4491,7 +4451,7 @@ 1 2 - 30128761 + 30132211 @@ -4507,7 +4467,7 @@ 1 2 - 30128761 + 30132211 @@ -4523,67 +4483,67 @@ 1 2 - 16464 + 16465 2 12 - 10819 + 10820 13 20 - 11760 + 11761 21 36 - 11289 + 11290 36 55 - 11289 + 11290 55 77 - 10819 + 10820 77 102 - 10819 + 10820 102 149 - 10819 + 10820 149 227 - 11289 + 11290 228 350 - 11289 + 10820 - 358 - 628 - 10819 + 351 + 604 + 10820 - 671 - 1926 - 10819 + 627 + 1479 + 10820 - 2168 + 1925 2380 - 1881 + 2352 @@ -4599,62 +4559,62 @@ 1 2 - 16464 + 16465 2 9 - 10819 + 10820 9 16 - 11760 + 11761 16 25 - 11289 + 11290 25 40 - 10819 + 10820 40 57 - 10819 + 10820 58 72 - 10819 + 10820 73 103 - 11289 + 11290 106 141 - 11760 + 11761 148 226 - 11289 + 11290 226 373 - 10819 + 10820 381 1456 - 10819 + 10820 1464 @@ -4675,62 +4635,62 @@ 1 2 - 16464 + 16465 2 4 - 8937 + 8938 4 5 - 7526 + 7527 5 6 - 7526 + 7527 6 8 - 11760 + 11761 8 13 - 12230 + 12231 13 17 - 10819 + 10820 17 25 - 11289 + 11290 25 31 - 11760 + 11761 31 38 - 10819 + 10820 38 52 - 10819 + 10820 52 64 - 10819 + 10820 65 @@ -4751,62 +4711,62 @@ 1 2 - 16464 + 16465 2 9 - 10819 + 10820 9 16 - 11760 + 11761 16 25 - 11289 + 11290 25 40 - 10819 + 10820 40 57 - 10819 + 10820 58 71 - 10819 + 10820 72 98 - 10819 + 10820 101 140 - 11760 + 11761 140 224 - 10819 + 10820 224 360 - 10819 + 10820 364 1185 - 10819 + 10820 1254 @@ -4827,27 +4787,27 @@ 1 2 - 16464 + 16465 2 10 - 11289 + 11290 10 14 - 10819 + 10820 14 21 - 11289 + 11290 22 31 - 11289 + 11290 31 @@ -4857,27 +4817,27 @@ 39 48 - 12230 + 12231 48 56 - 11760 + 11761 56 64 - 11760 + 11761 64 72 - 10819 + 10820 72 77 - 11289 + 11290 77 @@ -4898,47 +4858,47 @@ 1 2 - 581905 + 581935 2 3 - 318001 + 318018 3 4 - 199456 + 199466 4 6 - 160882 + 160890 6 10 - 190048 + 190058 10 16 - 166057 + 166065 16 25 - 168879 + 168888 25 46 - 163704 + 163713 46 169 - 159000 + 159009 170 @@ -4959,42 +4919,42 @@ 1 2 - 869799 + 869845 2 3 - 280838 + 280853 3 5 - 191459 + 191469 5 8 - 181110 + 181119 8 12 - 162293 + 162302 12 18 - 166527 + 166536 18 39 - 159941 + 159949 39 299 - 103021 + 103026 @@ -5010,47 +4970,47 @@ 1 2 - 612482 + 612514 2 3 - 313297 + 313313 3 4 - 202749 + 202760 4 6 - 184403 + 184412 6 9 - 180639 + 180649 9 13 - 166997 + 167006 13 19 - 173583 + 173592 19 29 - 167468 + 167476 29 52 - 113370 + 113376 @@ -5066,22 +5026,22 @@ 1 2 - 1545788 + 1545868 2 3 - 351401 + 351419 3 5 - 164645 + 164654 5 16 - 53157 + 53159 @@ -5097,47 +5057,47 @@ 1 2 - 586609 + 586639 2 3 - 318942 + 318958 3 4 - 201808 + 201819 4 6 - 167468 + 167476 6 9 - 160412 + 160420 9 14 - 178287 + 178297 14 21 - 176406 + 176415 21 32 - 163234 + 163243 32 61 - 159000 + 159009 61 @@ -5212,7 +5172,7 @@ 819 - 1546 + 1548 2822 @@ -5244,7 +5204,7 @@ 23 35 - 3292 + 3293 38 @@ -5263,23 +5223,23 @@ 73 - 83 - 2822 + 84 + 3293 - 83 - 95 - 2822 + 84 + 97 + 3293 - 96 + 98 101 - 3292 + 2352 101 105 - 3292 + 3293 106 @@ -5369,7 +5329,7 @@ 587 - 831 + 832 2822 @@ -5482,12 +5442,12 @@ 7 11 - 3292 + 3293 11 16 - 3292 + 3293 16 @@ -5502,7 +5462,7 @@ 24 27 - 3292 + 3293 28 @@ -5512,7 +5472,7 @@ 33 40 - 3292 + 3293 40 @@ -5553,52 +5513,52 @@ 1 2 - 589902 + 589932 2 3 - 312356 + 312372 3 4 - 198986 + 198996 4 6 - 161352 + 161361 6 10 - 189577 + 189587 10 16 - 163704 + 163713 16 25 - 171231 + 170770 25 45 - 159471 + 159949 45 160 - 159941 + 159949 160 299 - 11289 + 11290 @@ -5614,47 +5574,47 @@ 1 2 - 881560 + 881606 2 3 - 270019 + 270033 3 4 - 123249 + 123255 4 6 - 142065 + 142073 6 10 - 195693 + 195703 10 15 - 168409 + 168417 15 26 - 165586 + 165595 26 120 - 159471 + 159479 121 299 - 11760 + 11761 @@ -5670,22 +5630,22 @@ 1 2 - 1538732 + 1538812 2 3 - 349048 + 349067 3 5 - 172172 + 172181 5 10 - 57861 + 57864 @@ -5701,47 +5661,47 @@ 1 2 - 621890 + 621922 2 3 - 304359 + 304375 3 4 - 204631 + 204641 4 6 - 186755 + 186765 6 9 - 177817 + 177826 9 13 - 169349 + 169358 13 19 - 174524 + 174533 19 29 - 162764 + 162772 29 52 - 115722 + 115728 @@ -5757,47 +5717,47 @@ 1 2 - 596488 + 596519 2 3 - 311415 + 311431 3 4 - 198986 + 198996 4 6 - 171701 + 171710 6 9 - 157589 + 157597 9 14 - 173583 + 173592 14 21 - 180169 + 180178 21 32 - 165116 + 165124 32 60 - 159000 + 159009 60 @@ -5862,7 +5822,7 @@ 879 - 1081 + 1082 3763 @@ -5871,7 +5831,7 @@ 3763 - 1309 + 1310 1590 3763 @@ -5894,7 +5854,7 @@ 1 2 - 5644 + 5645 2 @@ -5970,7 +5930,7 @@ 1 2 - 5644 + 5645 2 @@ -6009,7 +5969,7 @@ 575 - 628 + 627 3763 @@ -6081,7 +6041,7 @@ 35 39 - 3292 + 3293 39 @@ -6122,7 +6082,7 @@ 1 2 - 5644 + 5645 2 @@ -6192,11 +6152,11 @@ locations_stmt - 3805462 + 3805465 id - 3805462 + 3805465 container @@ -6230,7 +6190,7 @@ 1 2 - 3805462 + 3805465 @@ -6246,7 +6206,7 @@ 1 2 - 3805462 + 3805465 @@ -6262,7 +6222,7 @@ 1 2 - 3805462 + 3805465 @@ -6278,7 +6238,7 @@ 1 2 - 3805462 + 3805465 @@ -6294,7 +6254,7 @@ 1 2 - 3805462 + 3805465 @@ -6958,7 +6918,7 @@ 10 11 - 10500 + 10501 11 @@ -8174,11 +8134,11 @@ locations_expr - 13128112 + 13128120 id - 13128112 + 13128120 container @@ -8212,7 +8172,7 @@ 1 2 - 13128112 + 13128120 @@ -8228,7 +8188,7 @@ 1 2 - 13128112 + 13128120 @@ -8244,7 +8204,7 @@ 1 2 - 13128112 + 13128120 @@ -8260,7 +8220,7 @@ 1 2 - 13128112 + 13128120 @@ -8276,7 +8236,7 @@ 1 2 - 13128112 + 13128120 @@ -10096,23 +10056,23 @@ numlines - 1406545 + 1406618 element_id - 1399488 + 1399561 num_lines - 102550 + 102556 num_code - 85615 + 85620 num_comment - 60213 + 60216 @@ -10126,7 +10086,7 @@ 1 2 - 1392432 + 1392505 2 @@ -10147,7 +10107,7 @@ 1 2 - 1393373 + 1393446 2 @@ -10168,7 +10128,7 @@ 1 2 - 1399488 + 1399561 @@ -10184,17 +10144,17 @@ 1 2 - 68680 + 68684 2 3 - 12230 + 12231 3 4 - 7526 + 7527 4 @@ -10220,12 +10180,12 @@ 1 2 - 71032 + 71036 2 3 - 12230 + 12231 3 @@ -10256,22 +10216,22 @@ 1 2 - 70092 + 70095 2 3 - 15053 + 15054 3 4 - 10819 + 10820 4 7 - 6585 + 6586 @@ -10287,22 +10247,22 @@ 1 2 - 53157 + 53159 2 3 - 14582 + 14583 3 5 - 6585 + 6586 5 42 - 6585 + 6586 44 @@ -10323,12 +10283,12 @@ 1 2 - 53157 + 53159 2 3 - 16934 + 16935 3 @@ -10338,7 +10298,7 @@ 5 8 - 6585 + 6586 8 @@ -10359,7 +10319,7 @@ 1 2 - 53627 + 53630 2 @@ -10369,7 +10329,7 @@ 3 5 - 7526 + 7527 5 @@ -10379,7 +10339,7 @@ 7 10 - 3292 + 3293 @@ -10395,7 +10355,7 @@ 1 2 - 34810 + 34812 2 @@ -10436,7 +10396,7 @@ 1 2 - 34810 + 34812 2 @@ -10477,7 +10437,7 @@ 1 2 - 34810 + 34812 2 @@ -10512,31 +10472,31 @@ diagnostics - 582773 + 72312 id - 582773 + 72312 severity - 209 + 23 error_tag - 7851 + 80 error_message - 56215 + 127 full_error_message - 582040 + 62738 location - 369218 + 150 @@ -10550,7 +10510,7 @@ 1 2 - 582773 + 72312 @@ -10566,7 +10526,7 @@ 1 2 - 582773 + 72312 @@ -10582,7 +10542,7 @@ 1 2 - 582773 + 72312 @@ -10598,7 +10558,7 @@ 1 2 - 582773 + 72312 @@ -10614,7 +10574,7 @@ 1 2 - 582773 + 72312 @@ -10628,14 +10588,14 @@ 12 - 254 - 255 - 104 + 4 + 5 + 11 - 5313 - 5314 - 104 + 6250 + 6251 + 11 @@ -10649,14 +10609,14 @@ 12 - 5 - 6 - 104 + 1 + 2 + 11 - 70 - 71 - 104 + 6 + 7 + 11 @@ -10669,508 +10629,388 @@ 12 - - 7 - 8 - 104 - - - 530 - 531 - 104 - - - - - - - severity - full_error_message - - - 12 - - - 254 - 255 - 104 - - - 5306 - 5307 - 104 - - - - - - - severity - location - - - 12 - - - 174 - 175 - 104 - - - 3429 - 3430 - 104 - - - - - - - error_tag - id - - - 12 - - - 1 - 2 - 1046 - - - 2 - 3 - 942 - 3 4 - 523 - - - 4 - 5 - 523 - - - 5 - 7 - 628 - - - 7 - 11 - 628 - - - 11 - 16 - 628 - - - 16 - 21 - 628 - - - 21 - 32 - 628 - - - 44 - 72 - 628 - - - 112 - 540 - 628 - - - 595 - 1254 - 418 - - - - - - - error_tag - severity - - - 12 - - - 1 - 2 - 7851 - - - - - - - error_tag - error_message - - - 12 - - - 1 - 2 - 5443 - - - 2 - 3 - 418 - - - 3 - 4 - 523 - - - 4 - 6 - 628 - - - 7 - 35 - 628 - - - 59 - 294 - 209 - - - - - - - error_tag - full_error_message - - - 12 - - - 1 - 2 - 1151 - - - 2 - 3 - 942 - - - 3 - 4 - 523 - - - 4 - 5 - 523 - - - 5 - 7 - 628 - - - 7 - 12 - 628 - - - 13 - 16 - 523 - - - 16 - 21 - 628 - - - 21 - 32 - 628 - - - 44 - 72 - 628 - - - 112 - 540 - 628 - - - 595 - 1254 - 418 - - - - - - - error_tag - location - - - 12 - - - 1 - 2 - 1674 - - - 2 - 3 - 942 - - - 3 - 4 - 418 - - - 4 - 5 - 523 - - - 5 - 6 - 523 - - - 6 - 7 - 523 - - - 7 - 15 - 628 - - - 15 - 20 - 628 - - - 23 - 44 - 628 - - - 44 - 95 - 628 - - - 139 - 630 - 628 - - - 764 - 765 - 104 - - - - - - - error_message - id - - - 12 - - - 1 - 2 - 24809 - - - 2 - 3 - 13608 - - - 3 - 4 - 4187 - - - 4 - 6 - 4606 - - - 6 - 14 - 4292 - - - 14 - 227 - 4292 - - - 405 - 1254 - 418 - - - - - - - error_message - severity - - - 12 - - - 1 - 2 - 56215 - - - - - - - error_message - error_tag - - - 12 - - - 1 - 2 - 56215 - - - - - - - error_message - full_error_message - - - 12 - - - 1 - 2 - 24914 - - - 2 - 3 - 13608 - - - 3 - 4 - 4187 - - - 4 - 6 - 4606 - - - 6 - 15 - 4396 - - - 15 - 540 - 4292 - - - 595 - 1254 - 209 - - - - - - - error_message - location - - - 12 - - - 1 - 2 - 36011 - - - 2 - 3 - 6909 - - - 3 - 5 - 4710 - - - 5 - 12 - 4292 - - - 12 - 765 - 4292 - - - - - - - full_error_message - id - - - 12 - - - 1 - 2 - 581935 + 11 8 9 - 104 + 11 + + + + + + + severity + full_error_message + + + 12 + + + 4 + 5 + 11 + + + 5422 + 5423 + 11 + + + + + + + severity + location + + + 12 + + + 4 + 5 + 11 + + + 9 + 10 + 11 + + + + + + + error_tag + id + + + 12 + + + 1 + 2 + 11 + + + 2 + 3 + 11 + + + 4 + 5 + 11 + + + 5 + 6 + 11 + + + 417 + 418 + 11 + + + 829 + 830 + 11 + + + 4996 + 4997 + 11 + + + + + + + error_tag + severity + + + 12 + + + 1 + 2 + 80 + + + + + + + error_tag + error_message + + + 12 + + + 1 + 2 + 57 + + + 3 + 4 + 23 + + + + + + + error_tag + full_error_message + + + 12 + + + 1 + 2 + 23 + + + 2 + 3 + 11 + + + 4 + 5 + 11 + + + 5 + 6 + 11 + + + 417 + 418 + 11 + + + 4996 + 4997 + 11 + + + + + + + error_tag + location + + + 12 + + + 1 + 2 + 46 + + + 2 + 3 + 11 + + + 4 + 5 + 11 + + + 5 + 6 + 11 + + + + + + + error_message + id + + + 12 + + + 1 + 2 + 34 + + + 2 + 3 + 23 + + + 5 + 6 + 11 + + + 10 + 11 + 11 + + + 75 + 76 + 11 + + + 332 + 333 + 11 + + + 829 + 830 + 11 + + + 4996 + 4997 + 11 + + + + + + + error_message + severity + + + 12 + + + 1 + 2 + 127 + + + + + + + error_message + error_tag + + + 12 + + + 1 + 2 + 127 + + + + + + + error_message + full_error_message + + + 12 + + + 1 + 2 + 46 + + + 2 + 3 + 23 + + + 5 + 6 + 11 + + + 10 + 11 + 11 + + + 75 + 76 + 11 + + + 332 + 333 + 11 + + + 4996 + 4997 + 11 + + + + + + + error_message + location + + + 12 + + + 1 + 2 + 92 + + + 2 + 3 + 23 + + + 5 + 6 + 11 + + + + + + + full_error_message + id + + + 12 + + + 1 + 2 + 62726 + + + 829 + 830 + 11 @@ -11186,7 +11026,7 @@ 1 2 - 582040 + 62738 @@ -11202,7 +11042,7 @@ 1 2 - 582040 + 62738 @@ -11218,7 +11058,7 @@ 1 2 - 582040 + 62738 @@ -11234,7 +11074,7 @@ 1 2 - 582040 + 62738 @@ -11250,17 +11090,12 @@ 1 2 - 206959 + 138 - 2 - 3 - 135774 - - - 3 - 13 - 26484 + 6242 + 6243 + 11 @@ -11276,12 +11111,7 @@ 1 2 - 361262 - - - 2 - 3 - 7955 + 150 @@ -11297,12 +11127,12 @@ 1 2 - 354458 + 138 - 2 - 6 - 14760 + 3 + 4 + 11 @@ -11318,12 +11148,12 @@ 1 2 - 353830 + 138 - 2 + 5 6 - 15388 + 11 @@ -11339,17 +11169,12 @@ 1 2 - 207063 + 138 - 2 - 3 - 135774 - - - 3 - 13 - 26380 + 5414 + 5415 + 11 @@ -11359,15 +11184,15 @@ files - 124189 + 124196 id - 124189 + 124196 name - 124189 + 124196 @@ -11381,7 +11206,7 @@ 1 2 - 124189 + 124196 @@ -11397,7 +11222,7 @@ 1 2 - 124189 + 124196 @@ -11455,7 +11280,7 @@ containerparent - 139243 + 139250 parent @@ -11463,7 +11288,7 @@ child - 139243 + 139250 @@ -11482,7 +11307,7 @@ 2 3 - 3292 + 3293 3 @@ -11518,7 +11343,7 @@ 1 2 - 139243 + 139250 @@ -11528,11 +11353,11 @@ fileannotations - 5254886 + 5253841 id - 5019 + 5018 kind @@ -11540,11 +11365,11 @@ name - 56112 + 56101 value - 47173 + 47163 @@ -11563,7 +11388,7 @@ 2 3 - 4845 + 4844 @@ -11624,7 +11449,7 @@ 936 937 - 1457 + 1456 1083 @@ -11690,7 +11515,7 @@ 1501 1502 - 1457 + 1456 1504 @@ -11779,12 +11604,12 @@ 1 2 - 9078 + 9076 2 3 - 6372 + 6370 3 @@ -11794,12 +11619,12 @@ 5 9 - 4371 + 4370 9 14 - 4082 + 4081 14 @@ -11809,27 +11634,27 @@ 18 20 - 4834 + 4833 20 34 - 4325 + 4324 34 128 - 4614 + 4613 128 229 - 4221 + 4220 229 387 - 4348 + 4347 387 @@ -11850,7 +11675,7 @@ 1 2 - 56112 + 56101 @@ -11866,17 +11691,17 @@ 1 2 - 9089 + 9088 2 3 - 8257 + 8255 3 4 - 2625 + 2624 4 @@ -11886,37 +11711,37 @@ 6 9 - 4232 + 4231 9 14 - 4313 + 4312 14 17 - 4232 + 4231 17 22 - 4706 + 4705 22 41 - 4313 + 4312 41 82 - 4267 + 4266 82 157 - 4209 + 4208 158 @@ -11937,7 +11762,7 @@ 1 2 - 7332 + 7330 2 @@ -11947,7 +11772,7 @@ 5 8 - 3411 + 3410 8 @@ -11957,22 +11782,22 @@ 15 17 - 2602 + 2601 17 19 - 4244 + 4243 19 34 - 3411 + 3410 34 189 - 3712 + 3711 189 @@ -11987,12 +11812,12 @@ 266 321 - 3770 + 3769 322 399 - 4047 + 4046 399 @@ -12013,7 +11838,7 @@ 1 2 - 47161 + 47152 2 @@ -12034,17 +11859,17 @@ 1 2 - 7355 + 7353 2 5 - 2648 + 2647 5 8 - 3596 + 3595 8 @@ -12059,17 +11884,17 @@ 17 19 - 3677 + 3676 19 29 - 3596 + 3595 29 39 - 3758 + 3757 39 @@ -12079,7 +11904,7 @@ 48 74 - 3654 + 3653 74 @@ -12089,7 +11914,7 @@ 102 119 - 3689 + 3688 119 @@ -12104,15 +11929,15 @@ inmacroexpansion - 109313317 + 109313388 id - 17941916 + 17941927 inv - 2682068 + 2682069 @@ -12126,32 +11951,32 @@ 1 3 - 1566018 + 1566019 3 5 - 1071344 + 1071345 5 6 - 1179814 + 1179815 6 7 - 4800000 + 4800003 7 8 - 6359380 + 6359384 8 9 - 2595248 + 2595250 9 @@ -12172,12 +11997,12 @@ 1 2 - 371855 + 371856 2 3 - 540116 + 540113 3 @@ -12212,7 +12037,7 @@ 11 337 - 223911 + 223913 339 @@ -12232,15 +12057,15 @@ affectedbymacroexpansion - 35540532 + 35540555 id - 5135277 + 5135280 inv - 2773181 + 2773183 @@ -12254,7 +12079,7 @@ 1 2 - 2804212 + 2804214 2 @@ -12279,7 +12104,7 @@ 12 50 - 405705 + 405706 50 @@ -12305,7 +12130,7 @@ 4 7 - 230823 + 230824 7 @@ -12315,7 +12140,7 @@ 9 12 - 250042 + 250043 12 @@ -12345,7 +12170,7 @@ 17 18 - 146328 + 146329 18 @@ -12370,19 +12195,19 @@ macroinvocations - 33894981 + 33889080 id - 33894981 + 33889080 macro_id - 81381 + 81423 location - 778557 + 778830 kind @@ -12400,7 +12225,7 @@ 1 2 - 33894981 + 33889080 @@ -12416,7 +12241,7 @@ 1 2 - 33894981 + 33889080 @@ -12432,7 +12257,7 @@ 1 2 - 33894981 + 33889080 @@ -12448,12 +12273,12 @@ 1 2 - 17578 + 17575 2 3 - 16977 + 16973 3 @@ -12463,42 +12288,42 @@ 4 5 - 4880 + 4879 5 8 - 6048 + 6047 8 14 - 6406 + 6440 14 29 - 6291 + 6313 29 - 72 - 6106 + 73 + 6220 - 72 - 247 - 6129 + 73 + 257 + 6139 - 248 - 4166 - 6106 + 257 + 5769 + 6116 - 4220 + 6272 168296 - 1156 + 1017 @@ -12514,32 +12339,32 @@ 1 2 - 43506 + 43498 2 3 - 10651 + 10649 3 4 - 5285 + 5284 4 6 - 6985 + 7018 6 13 - 6626 + 6636 13 66 - 6140 + 6151 66 @@ -12560,12 +12385,12 @@ 1 2 - 75553 + 75538 2 3 - 5828 + 5885 @@ -12581,37 +12406,37 @@ 1 2 - 320982 + 321115 2 3 - 177751 + 177878 3 4 - 47300 + 47313 4 5 - 59605 + 59616 5 9 - 68533 + 68542 9 23 - 58425 + 58414 23 244365 - 45958 + 45949 @@ -12627,12 +12452,12 @@ 1 2 - 731280 + 731563 2 350 - 47277 + 47267 @@ -12648,7 +12473,7 @@ 1 2 - 778557 + 778830 @@ -12662,13 +12487,13 @@ 12 - 20414 - 20415 + 20464 + 20465 11 - 2910446 - 2910447 + 2910469 + 2910470 11 @@ -12683,13 +12508,13 @@ 12 - 2123 - 2124 + 2128 + 2129 11 - 5418 - 5419 + 5423 + 5424 11 @@ -12704,13 +12529,13 @@ 12 - 6291 - 6292 + 6315 + 6316 11 - 61030 - 61031 + 61043 + 61044 11 @@ -12721,15 +12546,15 @@ macroparent - 30455592 + 30449647 id - 30455592 + 30449647 parent_id - 23698199 + 23693599 @@ -12743,7 +12568,7 @@ 1 2 - 30455592 + 30449647 @@ -12759,17 +12584,17 @@ 1 2 - 18307171 + 18303643 2 3 - 4540062 + 4539159 3 88 - 850965 + 850796 @@ -12779,15 +12604,15 @@ macrolocationbind - 3984640 + 3984515 id - 2778886 + 2778799 location - 1988454 + 1988392 @@ -12801,22 +12626,22 @@ 1 2 - 2183104 + 2183036 2 3 - 336443 + 336432 3 7 - 229815 + 229808 7 57 - 29523 + 29522 @@ -12832,22 +12657,22 @@ 1 2 - 1589588 + 1589539 2 3 - 169647 + 169641 3 8 - 154233 + 154228 8 723 - 74984 + 74982 @@ -12857,11 +12682,11 @@ macro_argument_unexpanded - 86176782 + 86159621 invocation - 26568343 + 26563044 argument_index @@ -12869,7 +12694,7 @@ text - 326094 + 326029 @@ -12883,22 +12708,22 @@ 1 2 - 7436793 + 7435302 2 3 - 10861692 + 10859530 3 4 - 6256808 + 6255563 4 67 - 2013048 + 2012648 @@ -12914,22 +12739,22 @@ 1 2 - 7508010 + 7506504 2 3 - 11011561 + 11009369 3 4 - 6087232 + 6086021 4 67 - 1961538 + 1961148 @@ -12954,7 +12779,7 @@ 715085 - 2297335 + 2297334 34 @@ -12997,57 +12822,57 @@ 1 2 - 40858 + 40850 2 3 - 65607 + 65594 3 4 - 15184 + 15181 4 5 - 45102 + 45093 5 8 - 25569 + 25576 8 12 - 16075 + 16060 12 16 - 22297 + 22292 16 23 - 26518 + 26512 23 43 - 24748 + 24743 43 164 - 24459 + 24454 164 521384 - 19671 + 19667 @@ -13063,17 +12888,17 @@ 1 2 - 235830 + 235783 2 3 - 79728 + 79712 3 9 - 10535 + 10533 @@ -13083,11 +12908,11 @@ macro_argument_expanded - 86176782 + 86159621 invocation - 26568343 + 26563044 argument_index @@ -13095,7 +12920,7 @@ text - 197597 + 197580 @@ -13109,22 +12934,22 @@ 1 2 - 7436793 + 7435302 2 3 - 10861692 + 10859530 3 4 - 6256808 + 6255563 4 67 - 2013048 + 2012648 @@ -13140,22 +12965,22 @@ 1 2 - 10747743 + 10745593 2 3 - 9374139 + 9372273 3 4 - 5306998 + 5305941 4 9 - 1139462 + 1139235 @@ -13180,7 +13005,7 @@ 715085 - 2297335 + 2297334 34 @@ -13206,7 +13031,7 @@ 870 - 13877 + 13879 46 @@ -13223,62 +13048,62 @@ 1 2 - 24552 + 24547 2 3 - 41147 + 41151 3 4 - 6927 + 6925 4 5 - 16364 + 16361 5 6 - 2995 + 2994 6 7 - 23291 + 23286 7 9 - 15982 + 15991 9 15 - 16699 + 16696 15 31 - 15589 + 15586 31 97 - 15080 + 15077 97 775 - 15485 + 15482 775 - 1052916 - 3481 + 1052906 + 3480 @@ -13294,17 +13119,17 @@ 1 2 - 99989 + 99992 2 3 - 82850 + 82834 3 66 - 14756 + 14753 @@ -13314,19 +13139,19 @@ functions - 4726273 + 4726519 id - 4726273 + 4726519 name - 1934352 + 1934453 kind - 3292 + 3293 @@ -13340,7 +13165,7 @@ 1 2 - 4726273 + 4726519 @@ -13356,7 +13181,7 @@ 1 2 - 4726273 + 4726519 @@ -13372,22 +13197,22 @@ 1 2 - 1516622 + 1516701 2 3 - 154296 + 154304 3 5 - 151003 + 151011 5 1724 - 112429 + 112435 @@ -13403,7 +13228,7 @@ 1 2 - 1933881 + 1933982 2 @@ -13510,15 +13335,15 @@ function_entry_point - 1176981 + 1177043 id - 1167103 + 1167163 entry_point - 1176981 + 1177043 @@ -13532,12 +13357,12 @@ 1 2 - 1157224 + 1157284 2 3 - 9878 + 9879 @@ -13553,7 +13378,7 @@ 1 2 - 1176981 + 1177043 @@ -13563,15 +13388,15 @@ function_return_type - 4734741 + 4734987 id - 4726273 + 4726519 return_type - 1016569 + 1016622 @@ -13585,7 +13410,7 @@ 1 2 - 4719217 + 4719463 2 @@ -13606,22 +13431,22 @@ 1 2 - 523103 + 523130 2 3 - 390445 + 390465 3 11 - 78559 + 78563 11 2516 - 24461 + 24462 @@ -13639,7 +13464,7 @@ traits - 1 + 2 handle @@ -13707,9 +13532,9 @@ 12 - 2 - 3 - 1 + 1 + 2 + 2 @@ -13723,9 +13548,9 @@ 12 - 2 - 3 - 1 + 1 + 2 + 2 @@ -13739,9 +13564,9 @@ 12 - 2 - 3 - 1 + 1 + 2 + 2 @@ -13943,48 +13768,48 @@ purefunctions - 99446 + 99447 id - 99446 + 99447 function_deleted - 140654 + 140661 id - 140654 + 140661 function_defaulted - 74325 + 74329 id - 74325 + 74329 member_function_this_type - 553641 + 553316 id - 553641 + 553316 this_type - 189690 + 189579 @@ -13998,7 +13823,7 @@ 1 2 - 553641 + 553316 @@ -14014,32 +13839,32 @@ 1 2 - 68526 + 68486 2 3 - 45414 + 45387 3 4 - 30475 + 30458 4 5 - 15537 + 15528 5 7 - 15607 + 15598 7 66 - 14128 + 14119 @@ -14049,27 +13874,27 @@ fun_decls - 5102136 + 5102402 id - 5096962 + 5097227 function - 4578563 + 4578801 type_id - 1013276 + 1013329 name - 1836035 + 1836130 location - 3461324 + 3461504 @@ -14083,7 +13908,7 @@ 1 2 - 5096962 + 5097227 @@ -14099,7 +13924,7 @@ 1 2 - 5091787 + 5092052 2 @@ -14120,7 +13945,7 @@ 1 2 - 5096962 + 5097227 @@ -14136,7 +13961,7 @@ 1 2 - 5096962 + 5097227 @@ -14152,17 +13977,17 @@ 1 2 - 4141075 + 4141291 2 3 - 363161 + 363180 3 7 - 74325 + 74329 @@ -14178,12 +14003,12 @@ 1 2 - 4536696 + 4536932 2 5 - 41867 + 41869 @@ -14199,7 +14024,7 @@ 1 2 - 4578563 + 4578801 @@ -14215,12 +14040,12 @@ 1 2 - 4197996 + 4198214 2 4 - 379155 + 379175 4 @@ -14241,22 +14066,22 @@ 1 2 - 445954 + 445977 2 3 - 453481 + 453505 3 9 - 79500 + 79504 9 2768 - 34340 + 34342 @@ -14272,22 +14097,22 @@ 1 2 - 530629 + 530657 2 3 - 381978 + 381998 3 11 - 77148 + 77152 11 2477 - 23520 + 23522 @@ -14303,17 +14128,17 @@ 1 2 - 883912 + 883958 2 5 - 90319 + 90324 5 822 - 39044 + 39046 @@ -14329,22 +14154,22 @@ 1 2 - 779480 + 779520 2 3 - 133127 + 133134 3 11 - 78089 + 78093 11 2030 - 22579 + 22581 @@ -14360,27 +14185,27 @@ 1 2 - 1245192 + 1245257 2 3 - 269548 + 269562 3 4 - 80441 + 80445 4 6 - 138772 + 138780 6 1758 - 102080 + 102085 @@ -14396,22 +14221,22 @@ 1 2 - 1425832 + 1425906 2 3 - 153355 + 153363 3 5 - 145358 + 145366 5 1708 - 111488 + 111494 @@ -14427,17 +14252,17 @@ 1 2 - 1615880 + 1615964 2 4 - 135009 + 135016 4 954 - 85145 + 85149 @@ -14453,27 +14278,27 @@ 1 2 - 1266831 + 1266897 2 3 - 296362 + 296377 3 4 - 79500 + 79504 4 8 - 139243 + 139250 8 664 - 54097 + 54100 @@ -14489,17 +14314,17 @@ 1 2 - 2995611 + 2995767 2 4 - 302007 + 302023 4 55 - 163704 + 163713 @@ -14515,17 +14340,17 @@ 1 2 - 3063351 + 3063511 2 6 - 268607 + 268621 6 55 - 129364 + 129371 @@ -14541,12 +14366,12 @@ 1 2 - 3245873 + 3246042 2 27 - 215450 + 215461 @@ -14562,12 +14387,12 @@ 1 2 - 3285388 + 3285559 2 13 - 175935 + 175944 @@ -14577,22 +14402,22 @@ fun_def - 1963988 + 1964090 id - 1963988 + 1964090 fun_specialized - 26343 + 26344 id - 26343 + 26344 @@ -14610,11 +14435,11 @@ fun_decl_specifiers - 2937280 + 2937433 id - 1710904 + 1710993 name @@ -14632,17 +14457,17 @@ 1 2 - 503345 + 503371 2 3 - 1188742 + 1188804 3 4 - 18816 + 18817 @@ -14814,26 +14639,26 @@ fun_decl_empty_throws - 1978101 + 1978204 fun_decl - 1978101 + 1978204 fun_decl_noexcept - 61239 + 61252 fun_decl - 61239 + 61252 constant - 61135 + 61148 @@ -14847,7 +14672,7 @@ 1 2 - 61239 + 61252 @@ -14863,7 +14688,7 @@ 1 2 - 61030 + 61043 2 @@ -14878,11 +14703,11 @@ fun_decl_empty_noexcept - 888146 + 888192 fun_decl - 888146 + 888192 @@ -14987,11 +14812,11 @@ param_decl_bind - 7472094 + 7472483 id - 7472094 + 7472483 index @@ -14999,7 +14824,7 @@ fun_decl - 4286434 + 4286657 @@ -15013,7 +14838,7 @@ 1 2 - 7472094 + 7472483 @@ -15029,7 +14854,7 @@ 1 2 - 7472094 + 7472483 @@ -15207,22 +15032,22 @@ 1 2 - 2409002 + 2409127 2 3 - 1071608 + 1071664 3 4 - 506638 + 506664 4 18 - 299184 + 299200 @@ -15238,22 +15063,22 @@ 1 2 - 2409002 + 2409127 2 3 - 1071608 + 1071664 3 4 - 506638 + 506664 4 18 - 299184 + 299200 @@ -15263,27 +15088,27 @@ var_decls - 8611913 + 8612362 id - 8543232 + 8543677 variable - 7520077 + 7520468 type_id - 2430641 + 2430768 name - 672695 + 672730 location - 5365099 + 5365378 @@ -15297,7 +15122,7 @@ 1 2 - 8543232 + 8543677 @@ -15313,12 +15138,12 @@ 1 2 - 8474552 + 8474993 2 3 - 68680 + 68684 @@ -15334,7 +15159,7 @@ 1 2 - 8543232 + 8543677 @@ -15350,7 +15175,7 @@ 1 2 - 8543232 + 8543677 @@ -15366,17 +15191,17 @@ 1 2 - 6658274 + 6658620 2 3 - 707035 + 707072 3 7 - 154767 + 154775 @@ -15392,12 +15217,12 @@ 1 2 - 7346963 + 7347346 2 4 - 173113 + 173122 @@ -15413,12 +15238,12 @@ 1 2 - 7402943 + 7403328 2 3 - 117133 + 117139 @@ -15434,12 +15259,12 @@ 1 2 - 6967337 + 6967700 2 4 - 552739 + 552768 @@ -15455,27 +15280,27 @@ 1 2 - 1505802 + 1505881 2 3 - 516046 + 516073 3 4 - 98787 + 98792 4 7 - 188636 + 188646 7 780 - 121367 + 121373 @@ -15491,22 +15316,22 @@ 1 2 - 1640342 + 1640427 2 3 - 491114 + 491140 3 7 - 188166 + 188176 7 742 - 111018 + 111024 @@ -15522,17 +15347,17 @@ 1 2 - 1918828 + 1918928 2 3 - 388563 + 388584 3 128 - 123249 + 123255 @@ -15548,22 +15373,22 @@ 1 2 - 1743833 + 1743924 2 3 - 406910 + 406931 3 8 - 190048 + 190058 8 595 - 89849 + 89854 @@ -15579,37 +15404,37 @@ 1 2 - 343874 + 343892 2 3 - 87497 + 87502 3 4 - 48923 + 48925 4 6 - 52216 + 52218 6 12 - 52686 + 52689 12 33 - 50804 + 50807 34 3281 - 36692 + 36694 @@ -15625,37 +15450,37 @@ 1 2 - 371628 + 371648 2 3 - 78559 + 78563 3 4 - 45630 + 45632 4 6 - 49864 + 49866 6 14 - 53627 + 53630 14 56 - 51275 + 51278 56 3198 - 22109 + 22110 @@ -15671,27 +15496,27 @@ 1 2 - 460537 + 460561 2 3 - 94553 + 94558 3 5 - 47041 + 47044 5 19 - 51275 + 51278 19 1979 - 19287 + 19288 @@ -15707,32 +15532,32 @@ 1 2 - 381978 + 381998 2 3 - 91260 + 91265 3 5 - 60213 + 60216 5 9 - 51745 + 51748 9 21 - 50804 + 50807 21 1020 - 36692 + 36694 @@ -15748,17 +15573,17 @@ 1 2 - 4535755 + 4535991 2 3 - 550387 + 550415 3 1783 - 278956 + 278971 @@ -15774,17 +15599,17 @@ 1 2 - 4939842 + 4940100 2 17 - 414436 + 414458 17 1779 - 10819 + 10820 @@ -15800,12 +15625,12 @@ 1 2 - 5016520 + 5016782 2 1561 - 348578 + 348596 @@ -15821,7 +15646,7 @@ 1 2 - 5360865 + 5361144 2 @@ -15836,22 +15661,22 @@ var_def - 4083685 + 4083897 id - 4083685 + 4083897 var_decl_specifiers - 334936 + 334953 id - 334936 + 334953 name @@ -15869,7 +15694,7 @@ 1 2 - 334936 + 334953 @@ -15916,19 +15741,19 @@ type_decls - 3283977 + 3284148 id - 3283977 + 3284148 type_id - 3233172 + 3233340 location - 3204006 + 3204173 @@ -15942,7 +15767,7 @@ 1 2 - 3283977 + 3284148 @@ -15958,7 +15783,7 @@ 1 2 - 3283977 + 3284148 @@ -15974,12 +15799,12 @@ 1 2 - 3191305 + 3191471 2 5 - 41867 + 41869 @@ -15995,12 +15820,12 @@ 1 2 - 3191305 + 3191471 2 5 - 41867 + 41869 @@ -16016,12 +15841,12 @@ 1 2 - 3163080 + 3163244 2 20 - 40926 + 40928 @@ -16037,12 +15862,12 @@ 1 2 - 3163080 + 3163244 2 20 - 40926 + 40928 @@ -16052,33 +15877,33 @@ type_def - 2660675 + 2660813 id - 2660675 + 2660813 type_decl_top - 755959 + 755998 type_decl - 755959 + 755998 namespace_decls - 306972 + 306973 id - 306972 + 306973 namespace_id @@ -16086,11 +15911,11 @@ location - 306972 + 306973 bodylocation - 306972 + 306973 @@ -16104,7 +15929,7 @@ 1 2 - 306972 + 306973 @@ -16120,7 +15945,7 @@ 1 2 - 306972 + 306973 @@ -16136,7 +15961,7 @@ 1 2 - 306972 + 306973 @@ -16350,7 +16175,7 @@ 1 2 - 306972 + 306973 @@ -16366,7 +16191,7 @@ 1 2 - 306972 + 306973 @@ -16382,7 +16207,7 @@ 1 2 - 306972 + 306973 @@ -16398,7 +16223,7 @@ 1 2 - 306972 + 306973 @@ -16414,7 +16239,7 @@ 1 2 - 306972 + 306973 @@ -16430,7 +16255,7 @@ 1 2 - 306972 + 306973 @@ -16440,19 +16265,19 @@ usings - 374921 + 374941 id - 374921 + 374941 element_id - 318471 + 318488 location - 249791 + 249804 @@ -16466,7 +16291,7 @@ 1 2 - 374921 + 374941 @@ -16482,7 +16307,7 @@ 1 2 - 374921 + 374941 @@ -16498,12 +16323,12 @@ 1 2 - 263903 + 263917 2 3 - 53157 + 53159 3 @@ -16524,12 +16349,12 @@ 1 2 - 263903 + 263917 2 3 - 53157 + 53159 3 @@ -16550,22 +16375,22 @@ 1 2 - 203690 + 203700 2 4 - 11289 + 11290 4 5 - 31517 + 31519 5 11 - 3292 + 3293 @@ -16581,22 +16406,22 @@ 1 2 - 203690 + 203700 2 4 - 11289 + 11290 4 5 - 31517 + 31519 5 11 - 3292 + 3293 @@ -16606,15 +16431,15 @@ using_container - 478195 + 478100 parent - 11298 + 11296 child - 303207 + 303147 @@ -16684,17 +16509,17 @@ 1 2 - 223629 + 223585 2 3 - 52990 + 52979 3 11 - 24401 + 24396 13 @@ -16709,15 +16534,15 @@ static_asserts - 130417 + 130418 id - 130417 + 130418 condition - 130417 + 130418 message @@ -16743,7 +16568,7 @@ 1 2 - 130417 + 130418 @@ -16759,7 +16584,7 @@ 1 2 - 130417 + 130418 @@ -16775,7 +16600,7 @@ 1 2 - 130417 + 130418 @@ -16791,7 +16616,7 @@ 1 2 - 130417 + 130418 @@ -16807,7 +16632,7 @@ 1 2 - 130417 + 130418 @@ -16823,7 +16648,7 @@ 1 2 - 130417 + 130418 @@ -16839,7 +16664,7 @@ 1 2 - 130417 + 130418 @@ -16855,7 +16680,7 @@ 1 2 - 130417 + 130418 @@ -17327,15 +17152,15 @@ params - 6825742 + 6826097 id - 6660155 + 6660502 function - 3940208 + 3940413 index @@ -17343,7 +17168,7 @@ type_id - 2234478 + 2234594 @@ -17357,7 +17182,7 @@ 1 2 - 6660155 + 6660502 @@ -17373,7 +17198,7 @@ 1 2 - 6660155 + 6660502 @@ -17389,12 +17214,12 @@ 1 2 - 6535025 + 6535365 2 4 - 125130 + 125137 @@ -17410,22 +17235,22 @@ 1 2 - 2303158 + 2303278 2 3 - 960590 + 960640 3 4 - 433253 + 433276 4 18 - 243205 + 243217 @@ -17441,22 +17266,22 @@ 1 2 - 2303158 + 2303278 2 3 - 960590 + 960640 3 4 - 433253 + 433276 4 18 - 243205 + 243217 @@ -17472,22 +17297,22 @@ 1 2 - 2605636 + 2605772 2 3 - 831225 + 831269 3 4 - 349519 + 349537 4 12 - 153826 + 153834 @@ -17741,22 +17566,22 @@ 1 2 - 1525560 + 1525639 2 3 - 446425 + 446448 3 8 - 171701 + 171710 8 522 - 90790 + 90795 @@ -17772,22 +17597,22 @@ 1 2 - 1749008 + 1749099 2 3 - 250731 + 250745 3 9 - 169820 + 169829 9 506 - 64917 + 64920 @@ -17803,17 +17628,17 @@ 1 2 - 1801694 + 1801788 2 3 - 353282 + 353301 3 13 - 79500 + 79504 @@ -17823,7 +17648,7 @@ overrides - 159823 + 159824 new @@ -17919,7 +17744,7 @@ type_id - 326293 + 326294 name @@ -17937,7 +17762,7 @@ 1 2 - 1048372 + 1048373 2 @@ -18087,19 +17912,19 @@ globalvariables - 300716 + 318724 id - 300708 + 318724 type_id - 1405 + 7852 name - 294738 + 86905 @@ -18113,12 +17938,7 @@ 1 2 - 300700 - - - 2 - 3 - 8 + 318724 @@ -18134,7 +17954,7 @@ 1 2 - 300708 + 318724 @@ -18150,27 +17970,32 @@ 1 2 - 977 + 5130 2 3 - 159 + 209 3 - 7 - 114 + 4 + 628 - 7 - 77 - 106 + 4 + 9 + 628 - 83 - 169397 - 49 + 18 + 31 + 628 + + + 35 + 1226 + 628 @@ -18186,27 +18011,32 @@ 1 2 - 1010 + 5130 2 3 - 135 + 209 3 - 7 - 112 + 4 + 628 - 7 - 105 - 106 + 4 + 9 + 628 - 106 - 168448 - 42 + 14 + 25 + 628 + + + 35 + 209 + 628 @@ -18222,12 +18052,17 @@ 1 2 - 290989 + 75911 2 - 33 - 3749 + 11 + 6596 + + + 11 + 449 + 4397 @@ -18243,12 +18078,12 @@ 1 2 - 294142 + 76644 2 - 12 - 596 + 3 + 10261 @@ -18266,7 +18101,7 @@ type_id - 37905 + 37909 name @@ -18326,7 +18161,7 @@ 3 4 - 2479 + 2483 4 @@ -18336,12 +18171,12 @@ 7 18 - 2878 + 2874 18 15847 - 2513 + 2517 @@ -18367,7 +18202,7 @@ 3 5 - 2946 + 2950 5 @@ -18449,11 +18284,11 @@ autoderivation - 149488 + 149519 var - 149488 + 149519 derivation_type @@ -18471,7 +18306,7 @@ 1 2 - 149488 + 149519 @@ -18537,7 +18372,7 @@ name - 240334 + 240335 location @@ -19272,7 +19107,7 @@ 1 2 - 240334 + 240335 @@ -19288,7 +19123,7 @@ 1 2 - 240334 + 240335 @@ -19414,23 +19249,23 @@ builtintypes - 22109 + 22110 id - 22109 + 22110 name - 22109 + 22110 kind - 22109 + 22110 size - 3292 + 3293 sign @@ -19452,7 +19287,7 @@ 1 2 - 22109 + 22110 @@ -19468,7 +19303,7 @@ 1 2 - 22109 + 22110 @@ -19484,7 +19319,7 @@ 1 2 - 22109 + 22110 @@ -19500,7 +19335,7 @@ 1 2 - 22109 + 22110 @@ -19516,7 +19351,7 @@ 1 2 - 22109 + 22110 @@ -19532,7 +19367,7 @@ 1 2 - 22109 + 22110 @@ -19548,7 +19383,7 @@ 1 2 - 22109 + 22110 @@ -19564,7 +19399,7 @@ 1 2 - 22109 + 22110 @@ -19580,7 +19415,7 @@ 1 2 - 22109 + 22110 @@ -19596,7 +19431,7 @@ 1 2 - 22109 + 22110 @@ -19612,7 +19447,7 @@ 1 2 - 22109 + 22110 @@ -19628,7 +19463,7 @@ 1 2 - 22109 + 22110 @@ -19644,7 +19479,7 @@ 1 2 - 22109 + 22110 @@ -19660,7 +19495,7 @@ 1 2 - 22109 + 22110 @@ -19676,7 +19511,7 @@ 1 2 - 22109 + 22110 @@ -20126,15 +19961,15 @@ derivedtypes - 4413446 + 4413676 id - 4413446 + 4413676 name - 2205312 + 2205427 kind @@ -20142,7 +19977,7 @@ type_id - 2729356 + 2729498 @@ -20156,7 +19991,7 @@ 1 2 - 4413446 + 4413676 @@ -20172,7 +20007,7 @@ 1 2 - 4413446 + 4413676 @@ -20188,7 +20023,7 @@ 1 2 - 4413446 + 4413676 @@ -20204,17 +20039,17 @@ 1 2 - 1935763 + 1935864 2 5 - 171231 + 171240 5 1173 - 98317 + 98322 @@ -20230,7 +20065,7 @@ 1 2 - 2204371 + 2204486 2 @@ -20251,17 +20086,17 @@ 1 2 - 1935763 + 1935864 2 5 - 171231 + 171240 5 1155 - 98317 + 98322 @@ -20400,22 +20235,22 @@ 1 2 - 1685972 + 1686060 2 3 - 568733 + 568763 3 4 - 367395 + 367414 4 54 - 107254 + 107260 @@ -20431,22 +20266,22 @@ 1 2 - 1697262 + 1697350 2 3 - 561206 + 561236 3 4 - 364572 + 364591 4 54 - 106314 + 106319 @@ -20462,22 +20297,22 @@ 1 2 - 1690206 + 1690294 2 3 - 572496 + 572526 3 4 - 366454 + 366473 4 6 - 100198 + 100203 @@ -20487,11 +20322,11 @@ pointerishsize - 3314342 + 3312399 id - 3314342 + 3312399 size @@ -20513,7 +20348,7 @@ 1 2 - 3314342 + 3312399 @@ -20529,7 +20364,7 @@ 1 2 - 3314342 + 3312399 @@ -20603,19 +20438,19 @@ arraysizes - 71503 + 71507 id - 71503 + 71507 num_elements - 23520 + 23522 bytesize - 26343 + 26344 alignment @@ -20633,7 +20468,7 @@ 1 2 - 71503 + 71507 @@ -20649,7 +20484,7 @@ 1 2 - 71503 + 71507 @@ -20665,7 +20500,7 @@ 1 2 - 71503 + 71507 @@ -20686,7 +20521,7 @@ 2 3 - 15053 + 15054 3 @@ -20722,7 +20557,7 @@ 1 2 - 18346 + 18347 2 @@ -20753,7 +20588,7 @@ 1 2 - 18346 + 18347 2 @@ -20789,12 +20624,12 @@ 2 3 - 16934 + 16935 3 4 - 3292 + 3293 4 @@ -20820,12 +20655,12 @@ 1 2 - 21639 + 21640 2 3 - 3292 + 3293 3 @@ -20846,12 +20681,12 @@ 1 2 - 22109 + 22110 2 3 - 3292 + 3293 4 @@ -20954,15 +20789,15 @@ typedefbase - 1724181 + 1736730 id - 1724181 + 1736730 type_id - 803746 + 810523 @@ -20976,7 +20811,7 @@ 1 2 - 1724181 + 1736730 @@ -20992,22 +20827,22 @@ 1 2 - 623380 + 629130 2 3 - 84307 + 85019 3 6 - 64497 + 64576 6 5443 - 31560 + 31797 @@ -21017,19 +20852,19 @@ decltypes - 355640 + 355894 id - 23953 + 23951 expr - 355640 + 355894 base_type - 17181 + 17180 parentheses_would_change_meaning @@ -21047,12 +20882,12 @@ 1 2 - 5961 + 5960 2 3 - 7492 + 7491 3 @@ -21067,12 +20902,12 @@ 7 18 - 1999 + 1980 18 42 - 2017 + 2035 42 @@ -21093,7 +20928,7 @@ 1 2 - 23953 + 23951 @@ -21109,7 +20944,7 @@ 1 2 - 23953 + 23951 @@ -21125,7 +20960,7 @@ 1 2 - 355640 + 355894 @@ -21141,7 +20976,7 @@ 1 2 - 355640 + 355894 @@ -21157,7 +20992,7 @@ 1 2 - 355640 + 355894 @@ -21204,7 +21039,7 @@ 2 3 - 7348 + 7347 3 @@ -21245,7 +21080,7 @@ 1 2 - 17181 + 17180 @@ -21275,8 +21110,8 @@ 12 - 19747 - 19748 + 19762 + 19763 18 @@ -21303,15 +21138,15 @@ usertypes - 5342989 + 5343268 id - 5342989 + 5343268 name - 1383024 + 1383096 kind @@ -21329,7 +21164,7 @@ 1 2 - 5342989 + 5343268 @@ -21345,7 +21180,7 @@ 1 2 - 5342989 + 5343268 @@ -21361,27 +21196,27 @@ 1 2 - 1001986 + 1002039 2 3 - 161352 + 161361 3 7 - 107725 + 107730 7 80 - 104432 + 104437 80 885 - 7526 + 7527 @@ -21397,17 +21232,17 @@ 1 2 - 1240488 + 1240552 2 3 - 127012 + 127019 3 7 - 15523 + 15524 @@ -21549,11 +21384,11 @@ usertypesize - 1755594 + 1755685 id - 1755594 + 1755685 size @@ -21575,7 +21410,7 @@ 1 2 - 1755594 + 1755685 @@ -21591,7 +21426,7 @@ 1 2 - 1755594 + 1755685 @@ -21607,7 +21442,7 @@ 1 2 - 3292 + 3293 2 @@ -21755,11 +21590,11 @@ usertype_final - 9526 + 9528 id - 9526 + 9528 @@ -21819,15 +21654,15 @@ mangled_name - 5264430 + 5301398 id - 5264430 + 5301398 mangled_name - 1235313 + 1272072 @@ -21841,7 +21676,7 @@ 1 2 - 5264430 + 5301398 @@ -21857,32 +21692,32 @@ 1 2 - 731027 + 767759 2 3 - 178287 + 178297 3 4 - 84674 + 84679 4 - 6 - 86086 + 7 + 114787 - 6 - 13 - 93142 + 7 + 25 + 95499 - 13 + 25 885 - 62094 + 31049 @@ -21892,59 +21727,59 @@ is_pod_class - 554326 + 554216 id - 554326 + 554216 is_standard_layout_class - 1295997 + 1296064 id - 1295997 + 1296064 is_complete - 1694439 + 1694528 id - 1694439 + 1694528 is_class_template - 405028 + 405049 id - 405028 + 405049 class_instantiation - 1121943 + 1122001 to - 1121943 + 1122001 from - 170761 + 170770 @@ -21958,7 +21793,7 @@ 1 2 - 1121943 + 1122001 @@ -21974,42 +21809,42 @@ 1 2 - 58802 + 58805 2 3 - 30106 + 30108 3 4 - 16464 + 16465 4 5 - 14582 + 14583 5 7 - 15523 + 15524 7 13 - 13171 + 13172 13 29 - 13171 + 13172 30 84 - 8937 + 8938 @@ -22019,11 +21854,11 @@ class_template_argument - 2978113 + 2977520 type_id - 1355713 + 1355443 index @@ -22031,7 +21866,7 @@ arg_type - 863386 + 863214 @@ -22045,27 +21880,27 @@ 1 2 - 551562 + 551453 2 3 - 411593 + 411511 3 4 - 246019 + 245970 4 7 - 122356 + 122331 7 113 - 24182 + 24177 @@ -22081,22 +21916,22 @@ 1 2 - 577421 + 577306 2 3 - 424939 + 424854 3 4 - 257884 + 257833 4 113 - 95467 + 95448 @@ -22117,7 +21952,7 @@ 2 3 - 821 + 820 3 @@ -22163,7 +21998,7 @@ 2 3 - 821 + 820 3 @@ -22204,27 +22039,27 @@ 1 2 - 535649 + 535542 2 3 - 181070 + 181034 3 4 - 52573 + 52563 4 10 - 65688 + 65675 10 11334 - 28403 + 28397 @@ -22240,17 +22075,17 @@ 1 2 - 755682 + 755532 2 3 - 85741 + 85724 3 22 - 21961 + 21957 @@ -22260,11 +22095,11 @@ class_template_argument_value - 508520 + 508546 type_id - 316590 + 316606 index @@ -22272,7 +22107,7 @@ arg_value - 508520 + 508546 @@ -22286,12 +22121,12 @@ 1 2 - 261081 + 261094 2 3 - 53627 + 53630 3 @@ -22312,17 +22147,17 @@ 1 2 - 200397 + 200407 2 3 - 81852 + 81856 3 5 - 29165 + 29167 5 @@ -22405,7 +22240,7 @@ 1 2 - 508520 + 508546 @@ -22421,7 +22256,7 @@ 1 2 - 508520 + 508546 @@ -22431,15 +22266,15 @@ is_proxy_class_for - 65387 + 65391 id - 65387 + 65391 templ_param_id - 65387 + 65391 @@ -22453,7 +22288,7 @@ 1 2 - 65387 + 65391 @@ -22469,7 +22304,7 @@ 1 2 - 65387 + 65391 @@ -22479,11 +22314,11 @@ type_mentions - 4011508 + 4011511 id - 4011508 + 4011511 type_id @@ -22491,7 +22326,7 @@ location - 3978135 + 3978138 kind @@ -22509,7 +22344,7 @@ 1 2 - 4011508 + 4011511 @@ -22525,7 +22360,7 @@ 1 2 - 4011508 + 4011511 @@ -22541,7 +22376,7 @@ 1 2 - 4011508 + 4011511 @@ -22675,7 +22510,7 @@ 1 2 - 3944762 + 3944765 2 @@ -22696,7 +22531,7 @@ 1 2 - 3944762 + 3944765 2 @@ -22717,7 +22552,7 @@ 1 2 - 3978135 + 3978138 @@ -22775,26 +22610,26 @@ is_function_template - 1413601 + 1413674 id - 1413601 + 1413674 function_instantiation - 906422 + 905891 to - 906422 + 905891 from - 146002 + 145917 @@ -22808,7 +22643,7 @@ 1 2 - 906422 + 905891 @@ -22824,27 +22659,27 @@ 1 2 - 101152 + 101092 2 3 - 14480 + 14472 3 6 - 12014 + 12007 6 21 - 12049 + 12042 22 869 - 6306 + 6302 @@ -22854,11 +22689,11 @@ function_template_argument - 2339815 + 2338443 function_id - 1318394 + 1317621 index @@ -22866,7 +22701,7 @@ arg_type - 305041 + 304862 @@ -22880,22 +22715,22 @@ 1 2 - 679667 + 679268 2 3 - 388084 + 387856 3 4 - 179966 + 179861 4 15 - 70676 + 70634 @@ -22911,22 +22746,22 @@ 1 2 - 694852 + 694445 2 3 - 393404 + 393173 3 4 - 150970 + 150882 4 9 - 79167 + 79120 @@ -23074,32 +22909,32 @@ 1 2 - 186978 + 186868 2 3 - 44850 + 44824 3 5 - 23218 + 23204 5 16 - 23535 + 23521 16 107 - 23006 + 22993 108 955 - 3452 + 3450 @@ -23115,17 +22950,17 @@ 1 2 - 274918 + 274756 2 4 - 26001 + 25986 4 17 - 4122 + 4119 @@ -23135,11 +22970,11 @@ function_template_argument_value - 362998 + 362786 function_id - 181340 + 181234 index @@ -23147,7 +22982,7 @@ arg_value - 360356 + 360145 @@ -23161,12 +22996,12 @@ 1 2 - 171969 + 171868 2 8 - 9371 + 9366 @@ -23182,17 +23017,17 @@ 1 2 - 151428 + 151339 2 3 - 20681 + 20669 3 97 - 9230 + 9225 @@ -23330,12 +23165,12 @@ 1 2 - 357714 + 357504 2 3 - 2642 + 2640 @@ -23351,7 +23186,7 @@ 1 2 - 360356 + 360145 @@ -23361,26 +23196,26 @@ is_variable_template - 42082 + 47326 id - 42082 + 47326 variable_instantiation - 49201 + 258309 to - 49201 + 258309 from - 25019 + 26281 @@ -23394,7 +23229,7 @@ 1 2 - 49201 + 258309 @@ -23410,22 +23245,42 @@ 1 2 - 14236 + 11203 2 3 - 7746 + 3559 3 + 4 + 1675 + + + 4 + 6 + 1884 + + + 6 8 - 1988 + 1884 8 14 - 1046 + 2408 + + + 15 + 26 + 1989 + + + 32 + 371 + 1675 @@ -23435,11 +23290,11 @@ variable_template_argument - 329648 + 448035 variable_id - 26380 + 247943 index @@ -23447,7 +23302,7 @@ arg_type - 217532 + 217578 @@ -23461,22 +23316,22 @@ 1 2 - 16121 + 119469 2 3 - 5652 + 99889 3 4 - 3873 + 18218 4 17 - 732 + 10365 @@ -23492,52 +23347,22 @@ 1 2 - 5757 + 129206 2 3 - 5234 + 91408 3 - 4 - 2093 - - - 4 5 - 1256 + 22825 5 - 6 - 2407 - - - 6 - 8 - 2303 - - - 8 - 11 - 2198 - - - 11 - 18 - 2303 - - - 18 - 50 - 1988 - - - 66 - 516 - 837 + 17 + 4502 @@ -23551,43 +23376,53 @@ 12 - 1 - 2 + 11 + 12 104 - 2 - 3 + 22 + 23 628 - 3 - 4 - 418 - - - 5 - 6 - 209 - - - 27 - 28 + 29 + 30 104 - 42 - 43 + 30 + 31 + 314 + + + 44 + 45 104 - 79 - 80 + 93 + 94 104 - 248 - 249 + 222 + 223 + 104 + + + 588 + 589 + 104 + + + 1090 + 1091 + 104 + + + 1974 + 1975 104 @@ -23665,17 +23500,22 @@ 1 2 - 180579 + 171403 2 3 - 21878 + 25024 3 - 38 - 15074 + 8 + 17067 + + + 8 + 94 + 4083 @@ -23691,12 +23531,12 @@ 1 2 - 200259 + 200302 2 5 - 16854 + 16857 5 @@ -23711,11 +23551,11 @@ variable_template_argument_value - 15597 + 15810 variable_id - 2826 + 6596 index @@ -23723,7 +23563,7 @@ arg_value - 12038 + 12041 @@ -23737,12 +23577,12 @@ 1 2 - 2617 + 5968 2 3 - 209 + 628 @@ -23756,45 +23596,25 @@ 12 - 2 - 3 - 418 + 1 + 2 + 314 - 3 - 4 - 104 + 2 + 3 + 5235 4 5 - 1360 - - - 5 - 6 - 209 - - - 6 - 7 - 209 + 837 8 9 209 - - 12 - 17 - 209 - - - 20 - 21 - 104 - @@ -23806,24 +23626,24 @@ 12 - - 2 - 3 - 104 - 6 7 104 - 8 - 9 + 19 + 20 104 - 13 - 14 + 20 + 21 + 104 + + + 24 + 25 104 @@ -23871,12 +23691,12 @@ 1 2 - 8479 + 8271 2 3 - 3559 + 3769 @@ -23892,7 +23712,7 @@ 1 2 - 12038 + 12041 @@ -23902,15 +23722,15 @@ routinetypes - 546982 + 546661 id - 546982 + 546661 return_type - 285945 + 285778 @@ -23924,7 +23744,7 @@ 1 2 - 546982 + 546661 @@ -23940,17 +23760,17 @@ 1 2 - 249233 + 249087 2 3 - 21315 + 21303 3 3594 - 15396 + 15387 @@ -23960,11 +23780,11 @@ routinetypeargs - 993519 + 993571 routine - 429019 + 429042 index @@ -23972,7 +23792,7 @@ type_id - 229563 + 229575 @@ -23986,27 +23806,27 @@ 1 2 - 155707 + 155715 2 3 - 135479 + 135486 3 4 - 63976 + 63979 4 5 - 46100 + 46103 5 18 - 27754 + 27756 @@ -24022,27 +23842,27 @@ 1 2 - 185814 + 185824 2 3 - 135009 + 135016 3 4 - 59272 + 59275 4 5 - 33869 + 33871 5 11 - 15053 + 15054 @@ -24200,27 +24020,27 @@ 1 2 - 148651 + 148659 2 3 - 31047 + 31049 3 5 - 16934 + 16935 5 12 - 18346 + 18347 12 113 - 14582 + 14583 @@ -24236,17 +24056,17 @@ 1 2 - 174994 + 175004 2 3 - 31047 + 31049 3 6 - 18816 + 18817 6 @@ -24261,19 +24081,19 @@ ptrtomembers - 38103 + 38105 id - 38103 + 38105 type_id - 38103 + 38105 class_id - 15523 + 15524 @@ -24287,7 +24107,7 @@ 1 2 - 38103 + 38105 @@ -24303,7 +24123,7 @@ 1 2 - 38103 + 38105 @@ -24319,7 +24139,7 @@ 1 2 - 38103 + 38105 @@ -24335,7 +24155,7 @@ 1 2 - 38103 + 38105 @@ -24397,15 +24217,15 @@ specifiers - 24932 + 24933 id - 24932 + 24933 str - 24932 + 24933 @@ -24419,7 +24239,7 @@ 1 2 - 24932 + 24933 @@ -24435,7 +24255,7 @@ 1 2 - 24932 + 24933 @@ -24445,11 +24265,11 @@ typespecifiers - 1317166 + 1317234 type_id - 1298819 + 1298887 spec_id @@ -24467,12 +24287,12 @@ 1 2 - 1280473 + 1280540 2 3 - 18346 + 18347 @@ -24533,11 +24353,11 @@ funspecifiers - 13049498 + 13041848 func_id - 3975160 + 3972829 spec_id @@ -24555,27 +24375,27 @@ 1 2 - 314977 + 314792 2 3 - 544551 + 544231 3 4 - 1145438 + 1144767 4 5 - 1732127 + 1731112 5 8 - 238064 + 237925 @@ -24691,11 +24511,11 @@ varspecifiers - 2347848 + 2347970 var_id - 1255071 + 1255136 spec_id @@ -24713,22 +24533,22 @@ 1 2 - 735731 + 735769 2 3 - 203219 + 203230 3 4 - 58802 + 58805 4 5 - 257317 + 257331 @@ -24789,11 +24609,11 @@ attributes - 696354 + 696502 id - 696354 + 696502 kind @@ -24801,7 +24621,7 @@ name - 1674 + 1675 name_space @@ -24809,7 +24629,7 @@ location - 483847 + 483949 @@ -24823,7 +24643,7 @@ 1 2 - 696354 + 696502 @@ -24839,7 +24659,7 @@ 1 2 - 696354 + 696502 @@ -24855,7 +24675,7 @@ 1 2 - 696354 + 696502 @@ -24871,7 +24691,7 @@ 1 2 - 696354 + 696502 @@ -25088,7 +24908,7 @@ 1 2 - 1674 + 1675 @@ -25264,17 +25084,17 @@ 1 2 - 442497 + 442591 2 9 - 36848 + 36856 9 201 - 4501 + 4502 @@ -25290,7 +25110,7 @@ 1 2 - 483847 + 483949 @@ -25306,7 +25126,7 @@ 1 2 - 479555 + 479656 2 @@ -25327,7 +25147,7 @@ 1 2 - 483847 + 483949 @@ -25337,11 +25157,11 @@ attribute_args - 352341 + 352360 id - 352341 + 352360 kind @@ -25349,7 +25169,7 @@ attribute - 270489 + 270503 index @@ -25357,7 +25177,7 @@ location - 329291 + 329308 @@ -25371,7 +25191,7 @@ 1 2 - 352341 + 352360 @@ -25387,7 +25207,7 @@ 1 2 - 352341 + 352360 @@ -25403,7 +25223,7 @@ 1 2 - 352341 + 352360 @@ -25419,7 +25239,7 @@ 1 2 - 352341 + 352360 @@ -25534,12 +25354,12 @@ 1 2 - 204631 + 204641 2 3 - 49864 + 49866 3 @@ -25560,7 +25380,7 @@ 1 2 - 260140 + 260153 2 @@ -25581,12 +25401,12 @@ 1 2 - 204631 + 204641 2 3 - 49864 + 49866 3 @@ -25607,12 +25427,12 @@ 1 2 - 204631 + 204641 2 3 - 49864 + 49866 3 @@ -25732,12 +25552,12 @@ 1 2 - 315179 + 315195 2 16 - 14112 + 14113 @@ -25753,7 +25573,7 @@ 1 2 - 316590 + 316606 2 @@ -25774,12 +25594,12 @@ 1 2 - 315179 + 315195 2 16 - 14112 + 14113 @@ -25795,7 +25615,7 @@ 1 2 - 329291 + 329308 @@ -25805,15 +25625,15 @@ attribute_arg_value - 351871 + 351889 arg - 351871 + 351889 value - 34810 + 34812 @@ -25827,7 +25647,7 @@ 1 2 - 351871 + 351889 @@ -25843,12 +25663,12 @@ 1 2 - 16934 + 16935 2 3 - 12230 + 12231 3 @@ -25969,15 +25789,15 @@ typeattributes - 62496 + 62509 type_id - 62077 + 62090 spec_id - 62496 + 62509 @@ -25991,7 +25811,7 @@ 1 2 - 61658 + 61671 2 @@ -26012,7 +25832,7 @@ 1 2 - 62496 + 62509 @@ -26022,15 +25842,15 @@ funcattributes - 635532 + 635565 func_id - 447366 + 447389 spec_id - 635532 + 635565 @@ -26044,17 +25864,17 @@ 1 2 - 341522 + 341540 2 3 - 64917 + 64920 3 6 - 39985 + 39987 6 @@ -26075,7 +25895,7 @@ 1 2 - 635532 + 635565 @@ -26143,15 +25963,15 @@ stmtattributes - 1006 + 1005 stmt_id - 1006 + 1005 spec_id - 1006 + 1005 @@ -26165,7 +25985,7 @@ 1 2 - 1006 + 1005 @@ -26181,7 +26001,7 @@ 1 2 - 1006 + 1005 @@ -26191,15 +26011,15 @@ unspecifiedtype - 10352924 + 10353463 type_id - 10352924 + 10353463 unspecified_type_id - 6956047 + 6956409 @@ -26213,7 +26033,7 @@ 1 2 - 10352924 + 10353463 @@ -26229,17 +26049,17 @@ 1 2 - 4675939 + 4676182 2 3 - 2037843 + 2037950 3 147 - 242264 + 242277 @@ -26249,19 +26069,19 @@ member - 5134480 + 5131470 parent - 689567 + 689163 index - 8808 + 8802 child - 5070710 + 5067737 @@ -26275,42 +26095,42 @@ 1 3 - 18884 + 18873 3 4 - 390691 + 390462 4 5 - 39072 + 39049 5 7 - 53059 + 53028 7 10 - 52848 + 52817 10 16 - 57569 + 57535 16 30 - 52954 + 52923 30 251 - 24486 + 24472 @@ -26326,42 +26146,42 @@ 1 3 - 18884 + 18873 3 4 - 390656 + 390427 4 5 - 39107 + 39084 5 7 - 53165 + 53134 7 10 - 53165 + 53134 10 16 - 57323 + 57289 16 29 - 52742 + 52711 29 253 - 24521 + 24507 @@ -26377,17 +26197,17 @@ 1 2 - 1409 + 1408 2 3 - 810 + 809 3 4 - 951 + 950 5 @@ -26448,7 +26268,7 @@ 1 2 - 810 + 809 2 @@ -26458,7 +26278,7 @@ 3 4 - 1162 + 1161 4 @@ -26503,7 +26323,7 @@ 2770 19253 - 458 + 457 @@ -26519,7 +26339,7 @@ 1 2 - 5070710 + 5067737 @@ -26535,12 +26355,12 @@ 1 2 - 5008419 + 5005483 2 8 - 62290 + 62254 @@ -26550,15 +26370,15 @@ enclosingfunction - 121743 + 121719 child - 121743 + 121719 parent - 69504 + 69490 @@ -26572,7 +26392,7 @@ 1 2 - 121743 + 121719 @@ -26588,22 +26408,22 @@ 1 2 - 36695 + 36687 2 3 - 21591 + 21587 3 4 - 6106 + 6105 4 45 - 5111 + 5110 @@ -26613,15 +26433,15 @@ derivations - 402388 + 402152 derivation - 402388 + 402152 sub - 381883 + 381659 index @@ -26629,11 +26449,11 @@ super - 206461 + 206340 location - 38156 + 38134 @@ -26647,7 +26467,7 @@ 1 2 - 402388 + 402152 @@ -26663,7 +26483,7 @@ 1 2 - 402388 + 402152 @@ -26679,7 +26499,7 @@ 1 2 - 402388 + 402152 @@ -26695,7 +26515,7 @@ 1 2 - 402388 + 402152 @@ -26711,12 +26531,12 @@ 1 2 - 366733 + 366518 2 7 - 15149 + 15141 @@ -26732,12 +26552,12 @@ 1 2 - 366733 + 366518 2 7 - 15149 + 15141 @@ -26753,12 +26573,12 @@ 1 2 - 366733 + 366518 2 7 - 15149 + 15141 @@ -26774,12 +26594,12 @@ 1 2 - 366733 + 366518 2 7 - 15149 + 15141 @@ -26924,12 +26744,12 @@ 1 2 - 199133 + 199016 2 1225 - 7328 + 7324 @@ -26945,12 +26765,12 @@ 1 2 - 199133 + 199016 2 1225 - 7328 + 7324 @@ -26966,12 +26786,12 @@ 1 2 - 206003 + 205882 2 4 - 458 + 457 @@ -26987,12 +26807,12 @@ 1 2 - 202867 + 202748 2 108 - 3593 + 3591 @@ -27008,22 +26828,22 @@ 1 2 - 28326 + 28310 2 5 - 3135 + 3133 5 16 - 2994 + 2992 17 133 - 2994 + 2992 142 @@ -27044,22 +26864,22 @@ 1 2 - 28326 + 28310 2 5 - 3135 + 3133 5 16 - 2994 + 2992 17 133 - 2994 + 2992 142 @@ -27080,7 +26900,7 @@ 1 2 - 38156 + 38134 @@ -27096,22 +26916,22 @@ 1 2 - 30757 + 30739 2 5 - 3417 + 3415 5 55 - 2889 + 2887 60 420 - 1092 + 1091 @@ -27121,11 +26941,11 @@ derspecifiers - 404291 + 404054 der_id - 402001 + 401765 spec_id @@ -27143,12 +26963,12 @@ 1 2 - 399710 + 399476 2 3 - 2290 + 2288 @@ -27189,11 +27009,11 @@ direct_base_offsets - 373110 + 372891 der_id - 373110 + 372891 offset @@ -27211,7 +27031,7 @@ 1 2 - 373110 + 372891 @@ -27262,11 +27082,11 @@ virtual_base_offsets - 6661 + 6660 sub - 3677 + 3676 super @@ -27288,7 +27108,7 @@ 1 2 - 2891 + 2890 2 @@ -27319,7 +27139,7 @@ 1 2 - 3099 + 3098 2 @@ -27553,23 +27373,23 @@ frienddecls - 715075 + 714656 id - 715075 + 714656 type_id - 42384 + 42359 decl_id - 70182 + 70141 location - 6341 + 6338 @@ -27583,7 +27403,7 @@ 1 2 - 715075 + 714656 @@ -27599,7 +27419,7 @@ 1 2 - 715075 + 714656 @@ -27615,7 +27435,7 @@ 1 2 - 715075 + 714656 @@ -27631,47 +27451,47 @@ 1 2 - 6200 + 6197 2 3 - 13212 + 13204 3 6 - 2959 + 2957 6 10 - 3206 + 3204 10 17 - 3276 + 3274 17 24 - 3347 + 3345 25 36 - 3311 + 3309 37 55 - 3241 + 3239 55 103 - 3628 + 3626 @@ -27687,47 +27507,47 @@ 1 2 - 6200 + 6197 2 3 - 13212 + 13204 3 6 - 2959 + 2957 6 10 - 3206 + 3204 10 17 - 3276 + 3274 17 24 - 3347 + 3345 25 36 - 3311 + 3309 37 55 - 3241 + 3239 55 103 - 3628 + 3626 @@ -27743,12 +27563,12 @@ 1 2 - 40939 + 40915 2 13 - 1444 + 1443 @@ -27764,37 +27584,37 @@ 1 2 - 40481 + 40458 2 3 - 5883 + 5880 3 8 - 6024 + 6021 8 15 - 5425 + 5422 15 32 - 5284 + 5281 32 71 - 5284 + 5281 72 160 - 1796 + 1795 @@ -27810,37 +27630,37 @@ 1 2 - 40481 + 40458 2 3 - 5883 + 5880 3 8 - 6024 + 6021 8 15 - 5425 + 5422 15 32 - 5284 + 5281 32 71 - 5284 + 5281 72 160 - 1796 + 1795 @@ -27856,7 +27676,7 @@ 1 2 - 69513 + 69472 2 @@ -27877,7 +27697,7 @@ 1 2 - 5954 + 5950 2 @@ -27898,7 +27718,7 @@ 1 2 - 6200 + 6197 2 @@ -27919,7 +27739,7 @@ 1 2 - 5989 + 5985 2 @@ -27934,19 +27754,19 @@ comments - 8781270 + 8783134 id - 8781270 + 8783134 contents - 3342962 + 3343672 location - 8781270 + 8783134 @@ -27960,7 +27780,7 @@ 1 2 - 8781270 + 8783134 @@ -27976,7 +27796,7 @@ 1 2 - 8781270 + 8783134 @@ -27992,17 +27812,17 @@ 1 2 - 3058223 + 3058872 2 7 - 251135 + 251189 7 32784 - 33603 + 33610 @@ -28018,17 +27838,17 @@ 1 2 - 3058223 + 3058872 2 7 - 251135 + 251189 7 32784 - 33603 + 33610 @@ -28044,7 +27864,7 @@ 1 2 - 8781270 + 8783134 @@ -28060,7 +27880,7 @@ 1 2 - 8781270 + 8783134 @@ -28070,15 +27890,15 @@ commentbinding - 3145674 + 3145838 id - 2490384 + 2490514 element - 3068526 + 3068686 @@ -28092,12 +27912,12 @@ 1 2 - 2408061 + 2408187 2 97 - 82322 + 82327 @@ -28113,12 +27933,12 @@ 1 2 - 2991378 + 2991533 2 3 - 77148 + 77152 @@ -28128,15 +27948,15 @@ exprconv - 7003750 + 7003755 converted - 7003750 + 7003755 conversion - 7003750 + 7003755 @@ -28150,7 +27970,7 @@ 1 2 - 7003750 + 7003755 @@ -28166,7 +27986,7 @@ 1 2 - 7003750 + 7003755 @@ -28176,22 +27996,22 @@ compgenerated - 8494343 + 8493976 id - 8494343 + 8493976 synthetic_destructor_call - 133110 + 133104 element - 103484 + 103479 i @@ -28199,7 +28019,7 @@ destructor_call - 117766 + 117760 @@ -28213,12 +28033,12 @@ 1 2 - 85546 + 85542 2 3 - 11832 + 11831 3 @@ -28239,12 +28059,12 @@ 1 2 - 85546 + 85542 2 3 - 11832 + 11831 3 @@ -28417,7 +28237,7 @@ 1 2 - 115749 + 115743 2 @@ -28438,7 +28258,7 @@ 1 2 - 117766 + 117760 @@ -28486,7 +28306,7 @@ 1 2 - 8937 + 8938 2 @@ -28517,15 +28337,15 @@ namespacembrs - 2463100 + 2463228 parentid - 10819 + 10820 memberid - 2463100 + 2463228 @@ -28605,7 +28425,7 @@ 1 2 - 2463100 + 2463228 @@ -28615,11 +28435,11 @@ exprparents - 14152882 + 14152891 expr_id - 14152882 + 14152891 child_index @@ -28627,7 +28447,7 @@ parent_id - 9417999 + 9418005 @@ -28641,7 +28461,7 @@ 1 2 - 14152882 + 14152891 @@ -28657,7 +28477,7 @@ 1 2 - 14152882 + 14152891 @@ -28775,12 +28595,12 @@ 1 2 - 5388939 + 5388942 2 3 - 3692597 + 3692599 3 @@ -28801,12 +28621,12 @@ 1 2 - 5388939 + 5388942 2 3 - 3692597 + 3692599 3 @@ -28821,22 +28641,22 @@ expr_isload - 4981779 + 4981688 expr_id - 4981779 + 4981688 conversionkinds - 4220621 + 4220624 expr_id - 4220621 + 4220624 kind @@ -28854,7 +28674,7 @@ 1 2 - 4220621 + 4220624 @@ -28883,8 +28703,8 @@ 1 - 26289 - 26290 + 26287 + 26288 1 @@ -28893,8 +28713,8 @@ 1 - 4131029 - 4131030 + 4131034 + 4131035 1 @@ -28905,11 +28725,11 @@ iscall - 3078157 + 3078281 caller - 3078157 + 3078281 kind @@ -28927,7 +28747,7 @@ 1 2 - 3078157 + 3078281 @@ -28951,8 +28771,8 @@ 18 - 167025 - 167026 + 167040 + 167041 18 @@ -28963,11 +28783,11 @@ numtemplatearguments - 543303 + 543548 expr_id - 543303 + 543548 num @@ -28985,7 +28805,7 @@ 1 2 - 543303 + 543548 @@ -29014,8 +28834,8 @@ 18 - 29893 - 29894 + 29908 + 29909 18 @@ -29074,23 +28894,23 @@ namequalifiers - 1618961 + 1618866 id - 1618961 + 1618866 qualifiableelement - 1618961 + 1618866 qualifyingelement - 79675 + 79653 location - 282719 + 282705 @@ -29104,7 +28924,7 @@ 1 2 - 1618961 + 1618866 @@ -29120,7 +28940,7 @@ 1 2 - 1618961 + 1618866 @@ -29136,7 +28956,7 @@ 1 2 - 1618961 + 1618866 @@ -29152,7 +28972,7 @@ 1 2 - 1618961 + 1618866 @@ -29168,7 +28988,7 @@ 1 2 - 1618961 + 1618866 @@ -29184,7 +29004,7 @@ 1 2 - 1618961 + 1618866 @@ -29200,12 +29020,12 @@ 1 2 - 45546 + 45526 2 3 - 17163 + 17162 3 @@ -29236,12 +29056,12 @@ 1 2 - 45546 + 45526 2 3 - 17163 + 17162 3 @@ -29272,7 +29092,7 @@ 1 2 - 49725 + 49704 2 @@ -29308,32 +29128,32 @@ 1 2 - 91742 + 91737 2 3 - 25646 + 25644 3 4 - 42179 + 42177 4 6 - 12895 + 12894 6 7 - 89869 + 89865 7 2135 - 20387 + 20386 @@ -29349,32 +29169,32 @@ 1 2 - 91742 + 91737 2 3 - 25646 + 25644 3 4 - 42179 + 42177 4 6 - 12895 + 12894 6 7 - 89869 + 89865 7 2135 - 20387 + 20386 @@ -29390,17 +29210,17 @@ 1 2 - 125168 + 125162 2 3 - 52354 + 52352 3 4 - 96622 + 96618 4 @@ -29415,11 +29235,11 @@ varbind - 6006364 + 6006368 expr - 6006364 + 6006368 var @@ -29437,7 +29257,7 @@ 1 2 - 6006364 + 6006368 @@ -29508,15 +29328,15 @@ funbind - 3080553 + 3080676 expr - 3076933 + 3077056 fun - 514055 + 514013 @@ -29530,7 +29350,7 @@ 1 2 - 3073313 + 3073437 2 @@ -29551,32 +29371,32 @@ 1 2 - 306582 + 306531 2 3 - 78775 + 78789 3 4 - 37226 + 37224 4 7 - 43475 + 43473 7 38 - 38703 + 38701 38 4943 - 9293 + 9292 @@ -29586,11 +29406,11 @@ expr_allocator - 46541 + 46514 expr - 46541 + 46514 func @@ -29612,7 +29432,7 @@ 1 2 - 46541 + 46514 @@ -29628,7 +29448,7 @@ 1 2 - 46541 + 46514 @@ -29712,11 +29532,11 @@ expr_deallocator - 55314 + 55282 expr - 55314 + 55282 func @@ -29738,7 +29558,7 @@ 1 2 - 55314 + 55282 @@ -29754,7 +29574,7 @@ 1 2 - 55314 + 55282 @@ -29907,15 +29727,15 @@ expr_cond_true - 654499 + 654500 cond - 654499 + 654500 true - 654499 + 654500 @@ -29929,7 +29749,7 @@ 1 2 - 654499 + 654500 @@ -29945,7 +29765,7 @@ 1 2 - 654499 + 654500 @@ -30003,11 +29823,11 @@ values - 10646146 + 10646153 id - 10646146 + 10646153 str @@ -30025,7 +29845,7 @@ 1 2 - 10646146 + 10646153 @@ -30071,15 +29891,15 @@ valuetext - 4756729 + 4756702 id - 4756729 + 4756702 text - 703924 + 703921 @@ -30093,7 +29913,7 @@ 1 2 - 4756729 + 4756702 @@ -30114,17 +29934,17 @@ 2 3 - 102489 + 102490 3 7 - 56759 + 56757 7 425881 - 17147 + 17145 @@ -30134,15 +29954,15 @@ valuebind - 11083050 + 11083057 val - 10646146 + 10646153 expr - 11083050 + 11083057 @@ -30156,7 +29976,7 @@ 1 2 - 10232022 + 10232029 2 @@ -30177,7 +29997,7 @@ 1 2 - 11083050 + 11083057 @@ -30388,11 +30208,11 @@ bitfield - 20936 + 20941 id - 20936 + 20941 bits @@ -30414,7 +30234,7 @@ 1 2 - 20936 + 20941 @@ -30430,7 +30250,7 @@ 1 2 - 20936 + 20941 @@ -30574,23 +30394,23 @@ initialisers - 1731824 + 1732353 init - 1731824 + 1732353 var - 717748 + 721222 expr - 1731824 + 1732353 location - 390436 + 390438 @@ -30604,7 +30424,7 @@ 1 2 - 1731824 + 1732353 @@ -30620,7 +30440,7 @@ 1 2 - 1731824 + 1732353 @@ -30636,7 +30456,7 @@ 1 2 - 1731824 + 1732353 @@ -30652,22 +30472,17 @@ 1 2 - 629392 + 632463 2 16 - 31625 + 32109 16 25 - 56686 - - - 25 - 112 - 44 + 56649 @@ -30683,22 +30498,17 @@ 1 2 - 629392 + 632463 2 16 - 31625 + 32109 16 25 - 56686 - - - 25 - 112 - 44 + 56649 @@ -30714,12 +30524,12 @@ 1 2 - 717666 + 721215 2 - 4 - 81 + 3 + 6 @@ -30735,7 +30545,7 @@ 1 2 - 1731824 + 1732353 @@ -30751,7 +30561,7 @@ 1 2 - 1731824 + 1732353 @@ -30767,7 +30577,7 @@ 1 2 - 1731824 + 1732353 @@ -30783,7 +30593,7 @@ 1 2 - 317956 + 317957 2 @@ -30798,7 +30608,7 @@ 15 111459 - 17855 + 17856 @@ -30814,17 +30624,17 @@ 1 2 - 340955 + 340698 2 4 - 35579 + 35642 4 12738 - 13901 + 14096 @@ -30840,7 +30650,7 @@ 1 2 - 317956 + 317957 2 @@ -30855,7 +30665,7 @@ 15 111459 - 17855 + 17856 @@ -30876,15 +30686,15 @@ expr_ancestor - 121674 + 121668 exp - 121674 + 121668 ancestor - 84880 + 84876 @@ -30898,7 +30708,7 @@ 1 2 - 121674 + 121668 @@ -30914,12 +30724,12 @@ 1 2 - 61377 + 61374 2 3 - 16785 + 16784 3 @@ -30939,19 +30749,19 @@ exprs - 18300140 + 18300152 id - 18300140 + 18300152 kind - 3382 + 3380 location - 3561673 + 3559831 @@ -30965,7 +30775,7 @@ 1 2 - 18300140 + 18300152 @@ -30981,7 +30791,7 @@ 1 2 - 18300140 + 18300152 @@ -31050,13 +30860,13 @@ 281 - 6591 - 63491 + 6528 + 63599 281 - 78915 - 109590 + 79044 + 109592 70 @@ -31127,11 +30937,11 @@ 1051 - 14609 + 14618 281 - 16974 + 16981 32757 140 @@ -31149,32 +30959,32 @@ 1 2 - 1936933 + 1935551 2 3 - 816932 + 816946 3 4 - 247260 + 247397 4 8 - 280872 + 280461 8 - 136 - 267131 + 137 + 267010 - 136 + 137 54140 - 12542 + 12464 @@ -31190,22 +31000,22 @@ 1 2 - 2363808 + 2362352 2 3 - 873691 + 873426 3 6 - 307261 + 307151 6 25 - 16911 + 16901 @@ -31215,15 +31025,15 @@ expr_types - 18357789 + 18357798 id - 18300140 + 18300152 typeid - 829282 + 829243 value_category @@ -31241,12 +31051,12 @@ 1 2 - 18242562 + 18242577 2 5 - 57577 + 57574 @@ -31262,7 +31072,7 @@ 1 2 - 18300140 + 18300152 @@ -31278,42 +31088,42 @@ 1 2 - 293470 + 292376 2 3 - 160720 + 161054 3 4 - 69824 + 70343 4 5 - 60801 + 60978 5 7 - 66996 + 66993 7 12 - 65321 + 65336 12 35 - 62638 + 62653 35 - 78674 - 49509 + 78689 + 49506 @@ -31329,12 +31139,12 @@ 1 2 - 716180 + 715462 2 3 - 102314 + 102993 3 @@ -31353,18 +31163,18 @@ 12 - 11828 - 11829 + 11826 + 11827 18 - 253738 - 253739 + 253755 + 253756 18 - 750551 - 750552 + 750585 + 750586 18 @@ -31379,18 +31189,18 @@ 12 - 1446 - 1447 + 1484 + 1485 18 - 11978 - 11979 + 11980 + 11981 18 - 39501 - 39502 + 39499 + 39500 18 @@ -31401,15 +31211,15 @@ new_allocated_type - 47598 + 47571 expr - 47598 + 47571 type_id - 28150 + 28134 @@ -31423,7 +31233,7 @@ 1 2 - 47598 + 47571 @@ -31439,17 +31249,17 @@ 1 2 - 11767 + 11760 2 3 - 14903 + 14894 3 19 - 1479 + 1478 @@ -32069,15 +31879,15 @@ condition_decl_bind - 38595 + 38593 expr - 38595 + 38593 decl - 38595 + 38593 @@ -32091,7 +31901,7 @@ 1 2 - 38595 + 38593 @@ -32107,7 +31917,7 @@ 1 2 - 38595 + 38593 @@ -32117,15 +31927,15 @@ typeid_bind - 36430 + 36408 expr - 36430 + 36408 type_id - 16383 + 16373 @@ -32139,7 +31949,7 @@ 1 2 - 36430 + 36408 @@ -32155,7 +31965,7 @@ 1 2 - 15960 + 15950 3 @@ -32354,11 +32164,11 @@ lambdas - 21639 + 21640 expr - 21639 + 21640 default_capture @@ -32380,7 +32190,7 @@ 1 2 - 21639 + 21640 @@ -32396,7 +32206,7 @@ 1 2 - 21639 + 21640 @@ -32470,15 +32280,15 @@ lambda_capture - 28224 + 28226 id - 28224 + 28226 lambda - 20698 + 20699 index @@ -32486,7 +32296,7 @@ field - 28224 + 28226 captured_by_reference @@ -32512,7 +32322,7 @@ 1 2 - 28224 + 28226 @@ -32528,7 +32338,7 @@ 1 2 - 28224 + 28226 @@ -32544,7 +32354,7 @@ 1 2 - 28224 + 28226 @@ -32560,7 +32370,7 @@ 1 2 - 28224 + 28226 @@ -32576,7 +32386,7 @@ 1 2 - 28224 + 28226 @@ -32592,7 +32402,7 @@ 1 2 - 28224 + 28226 @@ -32608,12 +32418,12 @@ 1 2 - 13171 + 13172 2 3 - 7526 + 7527 @@ -32629,12 +32439,12 @@ 1 2 - 13171 + 13172 2 3 - 7526 + 7527 @@ -32650,12 +32460,12 @@ 1 2 - 13171 + 13172 2 3 - 7526 + 7527 @@ -32671,7 +32481,7 @@ 1 2 - 20698 + 20699 @@ -32687,7 +32497,7 @@ 1 2 - 20698 + 20699 @@ -32703,12 +32513,12 @@ 1 2 - 13171 + 13172 2 3 - 7526 + 7527 @@ -32840,7 +32650,7 @@ 1 2 - 28224 + 28226 @@ -32856,7 +32666,7 @@ 1 2 - 28224 + 28226 @@ -32872,7 +32682,7 @@ 1 2 - 28224 + 28226 @@ -32888,7 +32698,7 @@ 1 2 - 28224 + 28226 @@ -32904,7 +32714,7 @@ 1 2 - 28224 + 28226 @@ -32920,7 +32730,7 @@ 1 2 - 28224 + 28226 @@ -33349,19 +33159,19 @@ stmts - 4660299 + 4661289 id - 4660299 + 4661289 kind - 1988 + 1989 location - 2286915 + 2287401 @@ -33375,7 +33185,7 @@ 1 2 - 4660299 + 4661289 @@ -33391,7 +33201,7 @@ 1 2 - 4660299 + 4661289 @@ -33619,22 +33429,22 @@ 1 2 - 1892154 + 1892555 2 4 - 175972 + 176010 4 12 - 176182 + 176219 12 699 - 42606 + 42615 @@ -33650,12 +33460,12 @@ 1 2 - 2229653 + 2230127 2 8 - 57261 + 57274 @@ -33953,15 +33763,15 @@ constexpr_if_then - 52551 + 52562 constexpr_if_stmt - 52551 + 52562 then_id - 52551 + 52562 @@ -33975,7 +33785,7 @@ 1 2 - 52551 + 52562 @@ -33991,7 +33801,7 @@ 1 2 - 52551 + 52562 @@ -34001,15 +33811,15 @@ constexpr_if_else - 30881 + 30888 constexpr_if_stmt - 30881 + 30888 else_id - 30881 + 30888 @@ -34023,7 +33833,7 @@ 1 2 - 30881 + 30888 @@ -34039,7 +33849,7 @@ 1 2 - 30881 + 30888 @@ -34049,15 +33859,15 @@ while_body - 30207 + 30201 while_stmt - 30207 + 30201 body_id - 30207 + 30201 @@ -34071,7 +33881,7 @@ 1 2 - 30207 + 30201 @@ -34087,7 +33897,7 @@ 1 2 - 30207 + 30201 @@ -34097,15 +33907,15 @@ do_body - 148604 + 148599 do_stmt - 148604 + 148599 body_id - 148604 + 148599 @@ -34119,7 +33929,7 @@ 1 2 - 148604 + 148599 @@ -34135,7 +33945,7 @@ 1 2 - 148604 + 148599 @@ -34193,7 +34003,7 @@ switch_case - 191408 + 191399 switch_stmt @@ -34205,7 +34015,7 @@ case_id - 191408 + 191399 @@ -34361,7 +34171,7 @@ 32 33 - 1909 + 1908 33 @@ -34402,7 +34212,7 @@ 32 33 - 1909 + 1908 33 @@ -34433,7 +34243,7 @@ 1 2 - 191408 + 191399 @@ -34449,7 +34259,7 @@ 1 2 - 191408 + 191399 @@ -34699,19 +34509,19 @@ stmtparents - 4052305 + 4052323 id - 4052305 + 4052323 index - 12209 + 12210 parent - 1719463 + 1719470 @@ -34725,7 +34535,7 @@ 1 2 - 4052305 + 4052323 @@ -34741,7 +34551,7 @@ 1 2 - 4052305 + 4052323 @@ -34879,12 +34689,12 @@ 1 2 - 987317 + 987321 2 3 - 372963 + 372965 3 @@ -34894,7 +34704,7 @@ 4 6 - 111241 + 111242 6 @@ -34920,12 +34730,12 @@ 1 2 - 987317 + 987321 2 3 - 372963 + 372965 3 @@ -34935,7 +34745,7 @@ 4 6 - 111241 + 111242 6 @@ -34955,11 +34765,11 @@ ishandler - 59432 + 59429 block - 59432 + 59429 @@ -35528,15 +35338,15 @@ blockscope - 1438063 + 1438137 block - 1438063 + 1438137 enclosing - 1321870 + 1321939 @@ -35550,7 +35360,7 @@ 1 2 - 1438063 + 1438137 @@ -35566,12 +35376,12 @@ 1 2 - 1256011 + 1256077 2 13 - 65858 + 65861 @@ -35581,19 +35391,19 @@ jumpinfo - 253995 + 253987 id - 253995 + 253987 str - 21152 + 21151 target - 53046 + 53044 @@ -35607,7 +35417,7 @@ 1 2 - 253995 + 253987 @@ -35623,7 +35433,7 @@ 1 2 - 253995 + 253987 @@ -35685,7 +35495,7 @@ 1 2 - 16717 + 16716 2 @@ -35721,7 +35531,7 @@ 2 3 - 26428 + 26427 3 @@ -35736,7 +35546,7 @@ 5 8 - 4691 + 4690 8 @@ -35757,7 +35567,7 @@ 1 2 - 53046 + 53044 @@ -35767,19 +35577,19 @@ preprocdirects - 4431252 + 4432193 id - 4431252 + 4432193 kind - 1046 + 1047 location - 4428739 + 4429680 @@ -35793,7 +35603,7 @@ 1 2 - 4431252 + 4432193 @@ -35809,7 +35619,7 @@ 1 2 - 4431252 + 4432193 @@ -35947,7 +35757,7 @@ 1 2 - 4428635 + 4429575 25 @@ -35968,7 +35778,7 @@ 1 2 - 4428739 + 4429680 @@ -35978,15 +35788,15 @@ preprocpair - 1442296 + 1442371 begin - 1206147 + 1206210 elseelifend - 1442296 + 1442371 @@ -36000,12 +35810,12 @@ 1 2 - 985992 + 986044 2 3 - 209805 + 209816 3 @@ -36026,7 +35836,7 @@ 1 2 - 1442296 + 1442371 @@ -36036,41 +35846,41 @@ preproctrue - 782302 + 783284 branch - 782302 + 783284 preprocfalse - 327409 + 326956 branch - 327409 + 326956 preproctext - 3572638 + 3573396 id - 3572638 + 3573396 head - 2591544 + 2592094 body - 1515816 + 1516138 @@ -36084,7 +35894,7 @@ 1 2 - 3572638 + 3573396 @@ -36100,7 +35910,7 @@ 1 2 - 3572638 + 3573396 @@ -36116,12 +35926,12 @@ 1 2 - 2444464 + 2444983 2 740 - 147080 + 147111 @@ -36137,12 +35947,12 @@ 1 2 - 2529362 + 2529899 2 5 - 62181 + 62195 @@ -36158,17 +35968,17 @@ 1 2 - 1372191 + 1372482 2 6 - 113686 + 113710 6 11572 - 29939 + 29945 @@ -36184,17 +35994,17 @@ 1 2 - 1375227 + 1375519 2 7 - 114000 + 114024 7 2959 - 26589 + 26595 @@ -36204,15 +36014,15 @@ includes - 315649 + 315665 id - 315649 + 315665 included - 118074 + 118080 @@ -36226,7 +36036,7 @@ 1 2 - 315649 + 315665 @@ -36242,12 +36052,12 @@ 1 2 - 61624 + 61627 2 3 - 22109 + 22110 3 @@ -36262,7 +36072,7 @@ 6 14 - 8937 + 8938 14 @@ -36325,11 +36135,11 @@ link_parent - 40143068 + 40119536 element - 5115983 + 5112984 link_target @@ -36347,17 +36157,17 @@ 1 2 - 701934 + 701522 2 9 - 44146 + 44120 9 10 - 4369903 + 4367341 From bce253920c0ebda146bb9879f01604cefbc2d9fd Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Wed, 27 Jul 2022 10:16:55 +0200 Subject: [PATCH 369/505] C++: Fix `__builtin_shuffle` qldoc --- cpp/ql/lib/semmle/code/cpp/exprs/BuiltInOperations.qll | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cpp/ql/lib/semmle/code/cpp/exprs/BuiltInOperations.qll b/cpp/ql/lib/semmle/code/cpp/exprs/BuiltInOperations.qll index 866ed3c314a..d18f4bdcb66 100644 --- a/cpp/ql/lib/semmle/code/cpp/exprs/BuiltInOperations.qll +++ b/cpp/ql/lib/semmle/code/cpp/exprs/BuiltInOperations.qll @@ -502,7 +502,8 @@ class BuiltInOperationBuiltInShuffleVector extends BuiltInOperation, @builtinshu * for more information. * ``` * // Concatenate every other element of 4-element vectors V1 and V2. - * V3 = __builtin_shufflevector(V1, V2, {0, 2, 4, 6}); + * M = {0, 2, 4, 6}; + * V3 = __builtin_shuffle(V1, V2, M); * ``` */ class BuiltInOperationBuiltInShuffle extends BuiltInOperation, @builtinshuffle { From 5a59354d73ac1e643aafb5c31bb02b9d37776f2f Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Wed, 27 Jul 2022 10:21:40 +0200 Subject: [PATCH 370/505] C++: Minor clean up of the builtin operations qldoc --- .../code/cpp/exprs/BuiltInOperations.qll | 58 +++++++++---------- 1 file changed, 28 insertions(+), 30 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/exprs/BuiltInOperations.qll b/cpp/ql/lib/semmle/code/cpp/exprs/BuiltInOperations.qll index d18f4bdcb66..979c9c03940 100644 --- a/cpp/ql/lib/semmle/code/cpp/exprs/BuiltInOperations.qll +++ b/cpp/ql/lib/semmle/code/cpp/exprs/BuiltInOperations.qll @@ -1,5 +1,5 @@ /** - * Provides classes for modeling built-in operations. Built-in operations are + * Provides classes for modeling built-in operations. Built-in operations are * typically compiler specific and are used by libraries and generated code. */ @@ -120,8 +120,8 @@ class BuiltInNoOp extends BuiltInOperation, @noopexpr { /** * A C/C++ `__builtin_offsetof` built-in operation (used by some implementations - * of `offsetof`). The operation retains its semantics even in the presence - * of an overloaded `operator &`). This is a gcc/clang extension. + * of `offsetof`). The operation retains its semantics even in the presence + * of an overloaded `operator &`). This is a gcc/clang extension. * ``` * struct S { * int a, b; @@ -137,8 +137,8 @@ class BuiltInOperationBuiltInOffsetOf extends BuiltInOperation, @offsetofexpr { /** * A C/C++ `__INTADDR__` built-in operation (used by some implementations - * of `offsetof`). The operation retains its semantics even in the presence - * of an overloaded `operator &`). This is an EDG extension. + * of `offsetof`). The operation retains its semantics even in the presence + * of an overloaded `operator &`). This is an EDG extension. * ``` * struct S { * int a, b; @@ -479,8 +479,7 @@ class BuiltInOperationBuiltInTypesCompatibleP extends BuiltInOperation, @typesco /** * A clang `__builtin_shufflevector` expression. * - * It outputs a permutation of elements from one or two input vectors. - * Please see + * It outputs a permutation of elements from one or two input vectors. See * https://releases.llvm.org/3.7.0/tools/clang/docs/LanguageExtensions.html#langext-builtin-shufflevector * for more information. * ``` @@ -498,7 +497,7 @@ class BuiltInOperationBuiltInShuffleVector extends BuiltInOperation, @builtinshu * A gcc `__builtin_shuffle` expression. * * It outputs a permutation of elements from one or two input vectors. - * Please see https://gcc.gnu.org/onlinedocs/gcc/Vector-Extensions.html + * See https://gcc.gnu.org/onlinedocs/gcc/Vector-Extensions.html * for more information. * ``` * // Concatenate every other element of 4-element vectors V1 and V2. @@ -516,7 +515,7 @@ class BuiltInOperationBuiltInShuffle extends BuiltInOperation, @builtinshuffle { * A clang `__builtin_convertvector` expression. * * Allows for conversion of vectors of equal element count and compatible - * element types. Please see + * element types. See * https://releases.llvm.org/3.7.0/tools/clang/docs/LanguageExtensions.html#builtin-convertvector * for more information. * ``` @@ -679,10 +678,9 @@ class BuiltInOperationIsAssignable extends BuiltInOperation, @isassignable { * of the `` header). * * Returns `true` if the type is a primitive type, or a `class`, `struct` or - * `union` WITHOUT (1) virtual functions or base classes, (2) reference member - * variable or (3) multiple occurrences of base `class` objects, among other - * restrictions. Please see - * https://en.cppreference.com/w/cpp/named_req/StandardLayoutType + * `union` without (1) virtual functions or base classes, (2) reference member + * variable, or (3) multiple occurrences of base `class` objects, among other + * restrictions. See https://en.cppreference.com/w/cpp/named_req/StandardLayoutType * for more information. * ``` * bool v = __is_standard_layout(MyType); @@ -699,7 +697,7 @@ class BuiltInOperationIsStandardLayout extends BuiltInOperation, @isstandardlayo * implementations of the `` header). * * Returns `true` if instances of this type can be copied by trivial - * means. The copying is done in a manner similar to the `memcpy` + * means. The copying is done in a manner similar to the `memcpy` * function. */ class BuiltInOperationIsTriviallyCopyable extends BuiltInOperation, @istriviallycopyableexpr { @@ -713,7 +711,7 @@ class BuiltInOperationIsTriviallyCopyable extends BuiltInOperation, @istrivially * the `` header). * * Returns `true` if the type is a scalar type, a reference type or an array of - * literal types, among others. Please see + * literal types, among others. See * https://en.cppreference.com/w/cpp/named_req/LiteralType * for more information. * @@ -816,7 +814,7 @@ class BuiltInOperationIsNothrowConstructible extends BuiltInOperation, @isnothro } /** - * The `__has_finalizer` built-in operation. This is a Microsoft extension. + * The `__has_finalizer` built-in operation. This is a Microsoft extension. * * Returns `true` if the type defines a _finalizer_ `C::!C(void)`, to be called * from either the regular destructor or the garbage collector. @@ -831,10 +829,10 @@ class BuiltInOperationHasFinalizer extends BuiltInOperation, @hasfinalizerexpr { } /** - * The `__is_delegate` built-in operation. This is a Microsoft extension. + * The `__is_delegate` built-in operation. This is a Microsoft extension. * * Returns `true` if the function has been declared as a `delegate`, used in - * message forwarding. Please see + * message forwarding. See * https://docs.microsoft.com/en-us/cpp/extensions/delegate-cpp-component-extensions * for more information. */ @@ -845,9 +843,9 @@ class BuiltInOperationIsDelegate extends BuiltInOperation, @isdelegateexpr { } /** - * The `__is_interface_class` built-in operation. This is a Microsoft extension. + * The `__is_interface_class` built-in operation. This is a Microsoft extension. * - * Returns `true` if the type has been declared as an `interface`. Please see + * Returns `true` if the type has been declared as an `interface`. See * https://docs.microsoft.com/en-us/cpp/extensions/interface-class-cpp-component-extensions * for more information. */ @@ -858,9 +856,9 @@ class BuiltInOperationIsInterfaceClass extends BuiltInOperation, @isinterfacecla } /** - * The `__is_ref_array` built-in operation. This is a Microsoft extension. + * The `__is_ref_array` built-in operation. This is a Microsoft extension. * - * Returns `true` if the object passed in is a _platform array_. Please see + * Returns `true` if the object passed in is a _platform array_. See * https://docs.microsoft.com/en-us/cpp/extensions/arrays-cpp-component-extensions * for more information. * ``` @@ -875,9 +873,9 @@ class BuiltInOperationIsRefArray extends BuiltInOperation, @isrefarrayexpr { } /** - * The `__is_ref_class` built-in operation. This is a Microsoft extension. + * The `__is_ref_class` built-in operation. This is a Microsoft extension. * - * Returns `true` if the type is a _reference class_. Please see + * Returns `true` if the type is a _reference class_. See * https://docs.microsoft.com/en-us/cpp/extensions/classes-and-structs-cpp-component-extensions * for more information. * ``` @@ -892,10 +890,10 @@ class BuiltInOperationIsRefClass extends BuiltInOperation, @isrefclassexpr { } /** - * The `__is_sealed` built-in operation. This is a Microsoft extension. + * The `__is_sealed` built-in operation. This is a Microsoft extension. * * Returns `true` if a given class or virtual function is marked as `sealed`, - * meaning that it cannot be extended or overridden. The `sealed` keyword + * meaning that it cannot be extended or overridden. The `sealed` keyword * is similar to the C++11 `final` keyword. * ``` * ref class X sealed { @@ -910,7 +908,7 @@ class BuiltInOperationIsSealed extends BuiltInOperation, @issealedexpr { } /** - * The `__is_simple_value_class` built-in operation. This is a Microsoft extension. + * The `__is_simple_value_class` built-in operation. This is a Microsoft extension. * * Returns `true` if passed a value type that contains no references to the * garbage-collected heap. @@ -929,9 +927,9 @@ class BuiltInOperationIsSimpleValueClass extends BuiltInOperation, @issimplevalu } /** - * The `__is_value_class` built-in operation. This is a Microsoft extension. + * The `__is_value_class` built-in operation. This is a Microsoft extension. * - * Returns `true` if passed a value type. Please see + * Returns `true` if passed a value type. See * https://docs.microsoft.com/en-us/cpp/extensions/classes-and-structs-cpp-component-extensions * For more information. * ``` @@ -964,7 +962,7 @@ class BuiltInOperationIsFinal extends BuiltInOperation, @isfinalexpr { } /** - * The `__builtin_choose_expr` expression. This is a gcc/clang extension. + * The `__builtin_choose_expr` expression. This is a gcc/clang extension. * * The expression functions similarly to the ternary `?:` operator, except * that it is evaluated at compile-time. From a27b1ee33a897e4641d6b2d660ab284c4dc5298d Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Wed, 27 Jul 2022 10:27:43 +0200 Subject: [PATCH 371/505] C++: Improve `ErrorExpr` documentation to match current practise --- cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll b/cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll index 55a59cc9588..68973293425 100644 --- a/cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll +++ b/cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll @@ -596,9 +596,12 @@ class ParenthesisExpr extends Conversion, @parexpr { } /** - * A C/C++ expression that has not been resolved. + * A C/C++ expression that could not be resolved, or that can no longer be + * represented due to a database upgrade or downgrade. * - * It is assigned `ErroneousType` as its type. + * If the expression could not be resolved, it has type `ErroneousType`. In the + * case of a database upgrade or downgrade, the original type from before the + * upgrade or downgrade is kept if that type can be represented. */ class ErrorExpr extends Expr, @errorexpr { override string toString() { result = "" } From 50e1ffda645610ff7b8dcf924b630d558ff58350 Mon Sep 17 00:00:00 2001 From: Alex Denisov Date: Fri, 29 Jul 2022 10:19:13 +0200 Subject: [PATCH 372/505] Swift: put all the PCM traps into the same place --- swift/extractor/SwiftExtractor.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/swift/extractor/SwiftExtractor.cpp b/swift/extractor/SwiftExtractor.cpp index 666b7a90044..8ad2fe128ac 100644 --- a/swift/extractor/SwiftExtractor.cpp +++ b/swift/extractor/SwiftExtractor.cpp @@ -17,6 +17,7 @@ #include "swift/extractor/infra/TargetFile.h" using namespace codeql; +using namespace std::string_literals; static void archiveFile(const SwiftExtractorConfiguration& config, swift::SourceFile& file) { if (std::error_code ec = llvm::sys::fs::create_directories(config.trapDir)) { @@ -53,12 +54,16 @@ static std::string getFilename(swift::ModuleDecl& module, swift::SourceFile* pri if (primaryFile) { return primaryFile->getFilename().str(); } - // Several modules with different name might come from .pcm (clang module) files - // In this case we want to differentiate them - std::string filename = module.getModuleFilename().str(); - filename += "-"; - filename += module.getName().str(); - return filename; + // PCM clang module + if (module.isNonSwiftModule()) { + // Several modules with different name might come from .pcm (clang module) files + // In this case we want to differentiate them + std::string filename = "/pcms/"s + llvm::sys::path::filename(module.getModuleFilename()).str(); + filename += "-"; + filename += module.getName().str(); + return filename; + } + return module.getModuleFilename().str(); } static llvm::SmallVector getTopLevelDecls(swift::ModuleDecl& module, From 065fecc57e9a962c97d488ac121df2816f680082 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Fri, 29 Jul 2022 10:55:26 +0200 Subject: [PATCH 373/505] Swift: extract precompiled swiftmodule files Previously we were not extracting any `swiftmodule` file that was not a system or a built-in one. This was done to avoid re-extracting `swiftmodule` files that were built previously in the same build, but it turned out to be too eager, as there are legitimate cases where a non-system, non-built-in precompiled swift module can be used. An example of that is the `PackageDescription` module used in Swift Package Manager manifest files (`Package.swift`). We now relax the test and trigger module extraction on all loaded modules that do not have source files (we trigger source file extraction for those). The catch, is that we also create empty trap files for current output `swiftmodule` files (including possible alias locations set up by XCode). This means that if a following extractor run loads a previously built `swiftmodule` file, although it will trigger module extraction, this will however be skipped as it will find its target file already present (this is done via the `TargetFile` semantics). --- swift/extractor/SwiftExtractor.cpp | 37 ++++++++++++------- swift/extractor/SwiftExtractorConfiguration.h | 4 ++ swift/extractor/SwiftOutputRewrite.cpp | 12 +++++- swift/extractor/SwiftOutputRewrite.h | 5 ++- swift/extractor/main.cpp | 1 + .../partial-modules/Modules.expected | 1 + swift/integration-tests/runner.py | 2 + 7 files changed, 46 insertions(+), 16 deletions(-) diff --git a/swift/extractor/SwiftExtractor.cpp b/swift/extractor/SwiftExtractor.cpp index 666b7a90044..bbf2a2551fd 100644 --- a/swift/extractor/SwiftExtractor.cpp +++ b/swift/extractor/SwiftExtractor.cpp @@ -165,22 +165,31 @@ void codeql::extractSwiftFiles(const SwiftExtractorConfiguration& config, auto inputFiles = collectInputFilenames(compiler); auto modules = collectModules(compiler); + // we want to make sure any following extractor run will not try to extract things from + // the swiftmodule files we are creating in this run, as those things will already have been + // extracted from source with more information. We do this by creating empty trap files. + // TargetFile semantics will ensure any following run trying to extract that swiftmodule will just + // skip doing it + auto outputModuleTrapSuffix = "-" + compiler.getMainModule()->getName().str().str() + ".trap"; + for (const auto& output : config.outputSwiftModules) { + TargetFile::create(output + outputModuleTrapSuffix, config.trapDir, config.getTempTrapDir()); + } for (auto& module : modules) { - // We only extract system and builtin modules here as the other "user" modules can be built - // during the build process and then re-used at a later stage. In this case, we extract the - // user code twice: once during the module build in a form of a source file, and then as - // a pre-built module during building of the dependent source files. - if (module->isSystemModule() || module->isBuiltinModule()) { - extractDeclarations(config, compiler, *module); - } else { - for (auto file : module->getFiles()) { - auto sourceFile = llvm::dyn_cast(file); - if (!sourceFile || inputFiles.count(sourceFile->getFilename().str()) == 0) { - continue; - } - archiveFile(config, *sourceFile); - extractDeclarations(config, compiler, *module, sourceFile); + bool isFromSourceFile = false; + for (auto file : module->getFiles()) { + auto sourceFile = llvm::dyn_cast(file); + if (!sourceFile) { + continue; } + isFromSourceFile = true; + if (inputFiles.count(sourceFile->getFilename().str()) == 0) { + continue; + } + archiveFile(config, *sourceFile); + extractDeclarations(config, compiler, *module, sourceFile); + } + if (!isFromSourceFile) { + extractDeclarations(config, compiler, *module); } } } diff --git a/swift/extractor/SwiftExtractorConfiguration.h b/swift/extractor/SwiftExtractorConfiguration.h index 3365da5f268..eec73e7dc21 100644 --- a/swift/extractor/SwiftExtractorConfiguration.h +++ b/swift/extractor/SwiftExtractorConfiguration.h @@ -32,5 +32,9 @@ struct SwiftExtractorConfiguration { // A temporary directory that contains build artifacts generated by the extractor during the // overall extraction process. std::string getTempArtifactDir() const { return scratchDir + "/swift-extraction-artifacts"; } + + // Output swiftmodule files. This also includes possible locations where XCode internally moves + // modules + std::vector outputSwiftModules; }; } // namespace codeql diff --git a/swift/extractor/SwiftOutputRewrite.cpp b/swift/extractor/SwiftOutputRewrite.cpp index 35a38512ff8..f85290f5beb 100644 --- a/swift/extractor/SwiftOutputRewrite.cpp +++ b/swift/extractor/SwiftOutputRewrite.cpp @@ -163,7 +163,7 @@ static std::vector computeModuleAliases(llvm::StringRef modulePath, namespace codeql { std::unordered_map rewriteOutputsInPlace( - SwiftExtractorConfiguration& config, + const SwiftExtractorConfiguration& config, std::vector& CLIArgs) { std::unordered_map remapping; @@ -323,5 +323,15 @@ std::vector collectVFSFiles(const SwiftExtractorConfiguration& conf return overlays; } +std::vector getOutputSwiftModules( + const std::unordered_map& remapping) { + std::vector ret; + for (const auto& [oldPath, newPath] : remapping) { + if (llvm::StringRef(oldPath).endswith(".swiftmodule")) { + ret.push_back(oldPath); + } + } + return ret; +} } // namespace codeql diff --git a/swift/extractor/SwiftOutputRewrite.h b/swift/extractor/SwiftOutputRewrite.h index b7ee7fa3829..61ef2d98ea6 100644 --- a/swift/extractor/SwiftOutputRewrite.h +++ b/swift/extractor/SwiftOutputRewrite.h @@ -13,7 +13,7 @@ struct SwiftExtractorConfiguration; // artifacts produced by the actual Swift compiler. // Returns the map containing remapping oldpath -> newPath. std::unordered_map rewriteOutputsInPlace( - SwiftExtractorConfiguration& config, + const SwiftExtractorConfiguration& config, std::vector& CLIArgs); // Create directories for all the redirected new paths as the Swift compiler expects them to exist. @@ -29,4 +29,7 @@ void storeRemappingForVFS(const SwiftExtractorConfiguration& config, // This is separate from storeRemappingForVFS as we also collect files produced by other processes. std::vector collectVFSFiles(const SwiftExtractorConfiguration& config); +// Returns a list of output remapped swift module files +std::vector getOutputSwiftModules( + const std::unordered_map& remapping); } // namespace codeql diff --git a/swift/extractor/main.cpp b/swift/extractor/main.cpp index bde37b0ccb5..d9ad3475477 100644 --- a/swift/extractor/main.cpp +++ b/swift/extractor/main.cpp @@ -68,6 +68,7 @@ int main(int argc, char** argv) { codeql::rewriteOutputsInPlace(configuration, configuration.patchedFrontendOptions); codeql::ensureDirectoriesForNewPathsExist(remapping); codeql::storeRemappingForVFS(configuration, remapping); + configuration.outputSwiftModules = codeql::getOutputSwiftModules(remapping); std::vector args; for (auto& arg : configuration.patchedFrontendOptions) { diff --git a/swift/integration-tests/posix-only/partial-modules/Modules.expected b/swift/integration-tests/posix-only/partial-modules/Modules.expected index 4c738975f34..3cdcff9b980 100644 --- a/swift/integration-tests/posix-only/partial-modules/Modules.expected +++ b/swift/integration-tests/posix-only/partial-modules/Modules.expected @@ -1,4 +1,5 @@ | file://:0:0:0:0 | A | | file://:0:0:0:0 | B | +| file://:0:0:0:0 | PackageDescription | | file://:0:0:0:0 | main | | file://:0:0:0:0 | partial_modules | diff --git a/swift/integration-tests/runner.py b/swift/integration-tests/runner.py index b9e39325fd9..77a62bfb81b 100755 --- a/swift/integration-tests/runner.py +++ b/swift/integration-tests/runner.py @@ -62,6 +62,8 @@ def main(opts): ] if opts.check_databases: cmd.append("--check-databases") + else: + cmd.append("--no-check-databases") if opts.learn: cmd.append("--learn") cmd.extend(str(t.parent) for t in succesful_db_creation) From 604328ea5f69aeedc84ad63eebfc7d8eef769e0c Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Fri, 29 Jul 2022 12:25:11 +0200 Subject: [PATCH 374/505] Swift: strip suffix from swiftmodule trap files --- swift/extractor/SwiftExtractor.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/swift/extractor/SwiftExtractor.cpp b/swift/extractor/SwiftExtractor.cpp index 9d19d31c646..46204ee597e 100644 --- a/swift/extractor/SwiftExtractor.cpp +++ b/swift/extractor/SwiftExtractor.cpp @@ -56,8 +56,10 @@ static std::string getFilename(swift::ModuleDecl& module, swift::SourceFile* pri } // PCM clang module if (module.isNonSwiftModule()) { - // Several modules with different name might come from .pcm (clang module) files + // Several modules with different names might come from .pcm (clang module) files // In this case we want to differentiate them + // Moreover, pcm files may come from caches located in different directories, but are + // unambiguously identified by the base file name, so we can discard the absolute directory std::string filename = "/pcms/"s + llvm::sys::path::filename(module.getModuleFilename()).str(); filename += "-"; filename += module.getName().str(); @@ -175,9 +177,8 @@ void codeql::extractSwiftFiles(const SwiftExtractorConfiguration& config, // extracted from source with more information. We do this by creating empty trap files. // TargetFile semantics will ensure any following run trying to extract that swiftmodule will just // skip doing it - auto outputModuleTrapSuffix = "-" + compiler.getMainModule()->getName().str().str() + ".trap"; for (const auto& output : config.outputSwiftModules) { - TargetFile::create(output + outputModuleTrapSuffix, config.trapDir, config.getTempTrapDir()); + TargetFile::create(output, config.trapDir, config.getTempTrapDir()); } for (auto& module : modules) { bool isFromSourceFile = false; From 099ab0e0c2e6e3b4d64718b05339661f9290466e Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Fri, 29 Jul 2022 12:26:33 +0200 Subject: [PATCH 375/505] Swift: readd .trap suffix to swiftmodule trap files --- swift/extractor/SwiftExtractor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swift/extractor/SwiftExtractor.cpp b/swift/extractor/SwiftExtractor.cpp index 46204ee597e..debbc5c99af 100644 --- a/swift/extractor/SwiftExtractor.cpp +++ b/swift/extractor/SwiftExtractor.cpp @@ -178,7 +178,7 @@ void codeql::extractSwiftFiles(const SwiftExtractorConfiguration& config, // TargetFile semantics will ensure any following run trying to extract that swiftmodule will just // skip doing it for (const auto& output : config.outputSwiftModules) { - TargetFile::create(output, config.trapDir, config.getTempTrapDir()); + TargetFile::create(output + ".trap", config.trapDir, config.getTempTrapDir()); } for (auto& module : modules) { bool isFromSourceFile = false; From 6091f0dbce4da7e8f653e3d2eb4479819cbed00c Mon Sep 17 00:00:00 2001 From: Tony Torralba Date: Fri, 29 Jul 2022 13:44:11 +0200 Subject: [PATCH 376/505] Use camelCase for XML acronym --- java/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll | 2 +- java/ql/test/library-tests/xml/XMLTest.expected | 2 ++ java/ql/test/library-tests/xml/XMLTest.ql | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/java/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll b/java/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll index 7e4a1b65f4c..e61d3dabe70 100644 --- a/java/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll +++ b/java/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll @@ -21,7 +21,7 @@ private class KtExpectationComment extends KtComment, ExpectationComment { override string getContents() { result = this.getText().suffix(2).trim() } } -private class XMLExpectationComment extends ExpectationComment instanceof XMLComment { +private class XmlExpectationComment extends ExpectationComment instanceof XMLComment { override string getContents() { result = this.(XMLComment).getText().trim() } override Location getLocation() { result = this.(XMLComment).getLocation() } diff --git a/java/ql/test/library-tests/xml/XMLTest.expected b/java/ql/test/library-tests/xml/XMLTest.expected index e69de29bb2d..61d3c801963 100644 --- a/java/ql/test/library-tests/xml/XMLTest.expected +++ b/java/ql/test/library-tests/xml/XMLTest.expected @@ -0,0 +1,2 @@ +| test.xml:4:5:4:32 | attribute=value | Unexpected result: hasXmlResult= | +| test.xml:5:29:5:52 | $ hasXmlResult | Missing result:hasXmlResult= | \ No newline at end of file diff --git a/java/ql/test/library-tests/xml/XMLTest.ql b/java/ql/test/library-tests/xml/XMLTest.ql index e4b8b3a0361..fb51e0a1506 100644 --- a/java/ql/test/library-tests/xml/XMLTest.ql +++ b/java/ql/test/library-tests/xml/XMLTest.ql @@ -1,8 +1,8 @@ import semmle.code.xml.XML import TestUtilities.InlineExpectationsTest -class XMLTest extends InlineExpectationsTest { - XMLTest() { this = "XMLTest" } +class XmlTest extends InlineExpectationsTest { + XmlTest() { this = "XmlTest" } override string getARelevantTag() { result = "hasXmlResult" } From ec03ebbbfca2e7b5bce8cf0875cbe8c0b0628d95 Mon Sep 17 00:00:00 2001 From: Tony Torralba Date: Fri, 29 Jul 2022 13:44:25 +0200 Subject: [PATCH 377/505] Add spurious and missing test cases --- java/ql/test/library-tests/xml/test.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/java/ql/test/library-tests/xml/test.xml b/java/ql/test/library-tests/xml/test.xml index cd41bd7f36c..f0d9262f09f 100644 --- a/java/ql/test/library-tests/xml/test.xml +++ b/java/ql/test/library-tests/xml/test.xml @@ -1,4 +1,6 @@ Text + Text + Text \ No newline at end of file From 5b1fe56d5f03abdec92ba85e850bd9f8ecd2370c Mon Sep 17 00:00:00 2001 From: Alex Denisov Date: Fri, 29 Jul 2022 14:04:32 +0200 Subject: [PATCH 378/505] Swift: do not deduplicate PCM variables (as the mangler crashes there sometimes) --- swift/extractor/visitors/DeclVisitor.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/swift/extractor/visitors/DeclVisitor.cpp b/swift/extractor/visitors/DeclVisitor.cpp index dc693e66a0f..0a889fb42e1 100644 --- a/swift/extractor/visitors/DeclVisitor.cpp +++ b/swift/extractor/visitors/DeclVisitor.cpp @@ -115,7 +115,9 @@ codeql::PatternBindingDecl DeclVisitor::translatePatternBindingDecl( std::optional DeclVisitor::translateVarDecl(const swift::VarDecl& decl) { std::optional entry; - if (decl.getDeclContext()->isLocalContext()) { + // We do not deduplicate variables from non-swift (PCM, clang modules) modules as the mangler + // crashes sometimes + if (decl.getDeclContext()->isLocalContext() || decl.getModuleContext()->isNonSwiftModule()) { entry.emplace(dispatcher_.assignNewLabel(decl)); } else { entry = createNamedEntry(decl); From 34edb2537fd700451dece7fd76d48deb8fc14ab8 Mon Sep 17 00:00:00 2001 From: Alex Denisov Date: Fri, 29 Jul 2022 10:33:55 +0200 Subject: [PATCH 379/505] Swift: mangle TypeAliasDecls differently --- swift/extractor/visitors/DeclVisitor.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/swift/extractor/visitors/DeclVisitor.cpp b/swift/extractor/visitors/DeclVisitor.cpp index dc693e66a0f..05392711a54 100644 --- a/swift/extractor/visitors/DeclVisitor.cpp +++ b/swift/extractor/visitors/DeclVisitor.cpp @@ -296,6 +296,14 @@ std::string DeclVisitor::mangledName(const swift::ValueDecl& decl) { if (decl.getKind() == swift::DeclKind::Module) { return static_cast(decl).getRealName().str().str(); } + // In cases like this (when coming from PCM) + // typealias CFXMLTree = CFTree + // typealias CFXMLTreeRef = CFXMLTree + // mangleAnyDecl mangles both CFXMLTree and CFXMLTreeRef into 'So12CFXMLTreeRefa' + // which is not correct and causes inconsistencies. mangleEntity makes these two distinct + if (decl.getKind() == swift::DeclKind::TypeAlias) { + return mangler.mangleEntity(&decl); + } // prefix adds a couple of special symbols, we don't necessary need them return mangler.mangleAnyDecl(&decl, /* prefix = */ false); } From daf1fa3c31df2d21aa3cea0c812fc68c26614479 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Fri, 29 Jul 2022 16:27:55 +0200 Subject: [PATCH 380/505] Swift: lock built swiftmodule traps in main This should cover `-merge-modules` mode. Dumping of the configuration to the target files was moved to a separate pair of header/source files, as now it is also done in `SwiftOutputRewrite.cpp`. --- swift/extractor/BUILD.bazel | 12 +++------ swift/extractor/SwiftExtractor.cpp | 27 ++----------------- swift/extractor/SwiftExtractorConfiguration.h | 7 +++-- swift/extractor/SwiftOutputRewrite.cpp | 14 ++++++---- swift/extractor/SwiftOutputRewrite.h | 6 ++--- swift/extractor/TargetTrapFile.cpp | 23 ++++++++++++++++ swift/extractor/TargetTrapFile.h | 11 ++++++++ swift/extractor/main.cpp | 2 +- 8 files changed, 56 insertions(+), 46 deletions(-) create mode 100644 swift/extractor/TargetTrapFile.cpp create mode 100644 swift/extractor/TargetTrapFile.h diff --git a/swift/extractor/BUILD.bazel b/swift/extractor/BUILD.bazel index b22841ca819..bf5c9e65d52 100644 --- a/swift/extractor/BUILD.bazel +++ b/swift/extractor/BUILD.bazel @@ -2,14 +2,10 @@ load("//swift:rules.bzl", "swift_cc_binary") swift_cc_binary( name = "extractor", - srcs = [ - "SwiftOutputRewrite.cpp", - "SwiftOutputRewrite.h", - "SwiftExtractor.cpp", - "SwiftExtractor.h", - "SwiftExtractorConfiguration.h", - "main.cpp", - ], + srcs = glob([ + "*.h", + "*.cpp", + ]), visibility = ["//swift:__pkg__"], deps = [ "//swift/extractor/infra", diff --git a/swift/extractor/SwiftExtractor.cpp b/swift/extractor/SwiftExtractor.cpp index debbc5c99af..22f4f01a470 100644 --- a/swift/extractor/SwiftExtractor.cpp +++ b/swift/extractor/SwiftExtractor.cpp @@ -14,7 +14,7 @@ #include "swift/extractor/trap/generated/TrapClasses.h" #include "swift/extractor/trap/TrapDomain.h" #include "swift/extractor/visitors/SwiftVisitor.h" -#include "swift/extractor/infra/TargetFile.h" +#include "swift/extractor/TargetTrapFile.h" using namespace codeql; using namespace std::string_literals; @@ -80,20 +80,6 @@ static llvm::SmallVector getTopLevelDecls(swift::ModuleDecl& modul return ret; } -static void dumpArgs(TargetFile& out, const SwiftExtractorConfiguration& config) { - out << "/* extractor-args:\n"; - for (const auto& opt : config.frontendOptions) { - out << " " << std::quoted(opt) << " \\\n"; - } - out << "\n*/\n"; - - out << "/* swift-frontend-args:\n"; - for (const auto& opt : config.patchedFrontendOptions) { - out << " " << std::quoted(opt) << " \\\n"; - } - out << "\n*/\n"; -} - static void extractDeclarations(const SwiftExtractorConfiguration& config, swift::CompilerInstance& compiler, swift::ModuleDecl& module, @@ -103,12 +89,11 @@ static void extractDeclarations(const SwiftExtractorConfiguration& config, // The extractor can be called several times from different processes with // the same input file(s). Using `TargetFile` the first process will win, and the following // will just skip the work - auto trapTarget = TargetFile::create(filename + ".trap", config.trapDir, config.getTempTrapDir()); + auto trapTarget = createTargetTrapFile(config, filename); if (!trapTarget) { // another process arrived first, nothing to do for us return; } - dumpArgs(*trapTarget, config); TrapDomain trap{*trapTarget}; // TODO: remove this and recreate it with IPA when we have that @@ -172,14 +157,6 @@ void codeql::extractSwiftFiles(const SwiftExtractorConfiguration& config, auto inputFiles = collectInputFilenames(compiler); auto modules = collectModules(compiler); - // we want to make sure any following extractor run will not try to extract things from - // the swiftmodule files we are creating in this run, as those things will already have been - // extracted from source with more information. We do this by creating empty trap files. - // TargetFile semantics will ensure any following run trying to extract that swiftmodule will just - // skip doing it - for (const auto& output : config.outputSwiftModules) { - TargetFile::create(output + ".trap", config.trapDir, config.getTempTrapDir()); - } for (auto& module : modules) { bool isFromSourceFile = false; for (auto file : module->getFiles()) { diff --git a/swift/extractor/SwiftExtractorConfiguration.h b/swift/extractor/SwiftExtractorConfiguration.h index eec73e7dc21..cd4ed51cdcd 100644 --- a/swift/extractor/SwiftExtractorConfiguration.h +++ b/swift/extractor/SwiftExtractorConfiguration.h @@ -3,6 +3,8 @@ #include #include +#include "swift/extractor/infra/TargetFile.h" + namespace codeql { struct SwiftExtractorConfiguration { // The location for storing TRAP files to be imported by CodeQL engine. @@ -32,9 +34,6 @@ struct SwiftExtractorConfiguration { // A temporary directory that contains build artifacts generated by the extractor during the // overall extraction process. std::string getTempArtifactDir() const { return scratchDir + "/swift-extraction-artifacts"; } - - // Output swiftmodule files. This also includes possible locations where XCode internally moves - // modules - std::vector outputSwiftModules; }; + } // namespace codeql diff --git a/swift/extractor/SwiftOutputRewrite.cpp b/swift/extractor/SwiftOutputRewrite.cpp index f85290f5beb..b09a558d187 100644 --- a/swift/extractor/SwiftOutputRewrite.cpp +++ b/swift/extractor/SwiftOutputRewrite.cpp @@ -1,5 +1,6 @@ #include "SwiftOutputRewrite.h" #include "swift/extractor/SwiftExtractorConfiguration.h" +#include "swift/extractor/TargetTrapFile.h" #include #include @@ -323,15 +324,18 @@ std::vector collectVFSFiles(const SwiftExtractorConfiguration& conf return overlays; } -std::vector getOutputSwiftModules( - const std::unordered_map& remapping) { - std::vector ret; + +void lockOutputSwiftModuleTraps(const SwiftExtractorConfiguration& config, + const std::unordered_map& remapping) { for (const auto& [oldPath, newPath] : remapping) { if (llvm::StringRef(oldPath).endswith(".swiftmodule")) { - ret.push_back(oldPath); + if (auto target = createTargetTrapFile(config, oldPath)) { + *target << "// trap file deliberately empty\n" + "// this swiftmodule was created during the build, so its entities must have" + " been extracted directly from source files"; + } } } - return ret; } } // namespace codeql diff --git a/swift/extractor/SwiftOutputRewrite.h b/swift/extractor/SwiftOutputRewrite.h index 61ef2d98ea6..94f1eeb6aaa 100644 --- a/swift/extractor/SwiftOutputRewrite.h +++ b/swift/extractor/SwiftOutputRewrite.h @@ -29,7 +29,7 @@ void storeRemappingForVFS(const SwiftExtractorConfiguration& config, // This is separate from storeRemappingForVFS as we also collect files produced by other processes. std::vector collectVFSFiles(const SwiftExtractorConfiguration& config); -// Returns a list of output remapped swift module files -std::vector getOutputSwiftModules( - const std::unordered_map& remapping); +// Creates empty trap files for output swiftmodule files +void lockOutputSwiftModuleTraps(const SwiftExtractorConfiguration& config, + const std::unordered_map& remapping); } // namespace codeql diff --git a/swift/extractor/TargetTrapFile.cpp b/swift/extractor/TargetTrapFile.cpp new file mode 100644 index 00000000000..2275575ecfa --- /dev/null +++ b/swift/extractor/TargetTrapFile.cpp @@ -0,0 +1,23 @@ +#include "swift/extractor/TargetTrapFile.h" +#include +namespace codeql { +std::optional createTargetTrapFile(const SwiftExtractorConfiguration& configuration, + std::string_view target) { + std::string trap{target}; + trap += ".trap"; + auto ret = TargetFile::create(trap, configuration.trapDir, configuration.getTempTrapDir()); + if (ret) { + *ret << "/* extractor-args:\n"; + for (const auto& opt : configuration.frontendOptions) { + *ret << " " << std::quoted(opt) << " \\\n"; + } + *ret << "\n*/\n" + "/* swift-frontend-args:\n"; + for (const auto& opt : configuration.patchedFrontendOptions) { + *ret << " " << std::quoted(opt) << " \\\n"; + } + *ret << "\n*/\n"; + } + return ret; +} +} // namespace codeql diff --git a/swift/extractor/TargetTrapFile.h b/swift/extractor/TargetTrapFile.h new file mode 100644 index 00000000000..eb8de4c206f --- /dev/null +++ b/swift/extractor/TargetTrapFile.h @@ -0,0 +1,11 @@ +#pragma once + +#include "swift/extractor/infra/TargetFile.h" +#include "swift/extractor/SwiftExtractorConfiguration.h" + +namespace codeql { + +std::optional createTargetTrapFile(const SwiftExtractorConfiguration& configuration, + std::string_view target); + +} // namespace codeql diff --git a/swift/extractor/main.cpp b/swift/extractor/main.cpp index d9ad3475477..5f9afef4e81 100644 --- a/swift/extractor/main.cpp +++ b/swift/extractor/main.cpp @@ -68,7 +68,7 @@ int main(int argc, char** argv) { codeql::rewriteOutputsInPlace(configuration, configuration.patchedFrontendOptions); codeql::ensureDirectoriesForNewPathsExist(remapping); codeql::storeRemappingForVFS(configuration, remapping); - configuration.outputSwiftModules = codeql::getOutputSwiftModules(remapping); + codeql::lockOutputSwiftModuleTraps(configuration, remapping); std::vector args; for (auto& arg : configuration.patchedFrontendOptions) { From 45e14c96f2c984d67da7f321c7c729ef7a3c6c38 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Fri, 29 Jul 2022 16:36:08 +0200 Subject: [PATCH 381/505] Swift: extract `ModuleType` --- swift/codegen/schema.yml | 1 + swift/extractor/visitors/TypeVisitor.cpp | 10 ++++++++++ swift/extractor/visitors/TypeVisitor.h | 7 ++++--- .../lib/codeql/swift/generated/type/ModuleType.qll | 8 ++++++++ swift/ql/lib/swift.dbscheme | 3 ++- swift/ql/test/TestUtils.qll | 6 +++++- .../generated/decl/ModuleDecl/ModuleDecl.expected | 1 + .../decl/ModuleDecl/default_module_name.swift | 0 .../generated/type/ModuleType/MISSING_SOURCE.txt | 4 ---- .../generated/type/ModuleType/ModuleType.expected | 2 ++ .../generated/type/ModuleType/ModuleType.ql | 12 ++++++++++++ .../type/ModuleType/default_module_name.swift | 0 .../generated/type/ModuleType/modules.swift | 1 + 13 files changed, 46 insertions(+), 9 deletions(-) create mode 100644 swift/ql/test/extractor-tests/generated/decl/ModuleDecl/default_module_name.swift delete mode 100644 swift/ql/test/extractor-tests/generated/type/ModuleType/MISSING_SOURCE.txt create mode 100644 swift/ql/test/extractor-tests/generated/type/ModuleType/ModuleType.expected create mode 100644 swift/ql/test/extractor-tests/generated/type/ModuleType/ModuleType.ql create mode 100644 swift/ql/test/extractor-tests/generated/type/ModuleType/default_module_name.swift create mode 100644 swift/ql/test/extractor-tests/generated/type/ModuleType/modules.swift diff --git a/swift/codegen/schema.yml b/swift/codegen/schema.yml index f810bfec636..83e2c87ef4e 100644 --- a/swift/codegen/schema.yml +++ b/swift/codegen/schema.yml @@ -112,6 +112,7 @@ LValueType: ModuleType: _extends: Type + module: ModuleDecl PlaceholderType: _extends: Type diff --git a/swift/extractor/visitors/TypeVisitor.cpp b/swift/extractor/visitors/TypeVisitor.cpp index ddc253c90a5..d1c499a9a96 100644 --- a/swift/extractor/visitors/TypeVisitor.cpp +++ b/swift/extractor/visitors/TypeVisitor.cpp @@ -381,4 +381,14 @@ codeql::OpenedArchetypeType TypeVisitor::translateOpenedArchetypeType( fillArchetypeType(type, entry); return entry; } + +codeql::ModuleType TypeVisitor::translateModuleType(const swift::ModuleType& type) { + auto key = type.getModule()->getRealName().str().str(); + if (type.getModule()->isNonSwiftModule()) { + key += "|clang"; + } + auto entry = createTypeEntry(type, key); + entry.module = dispatcher_.fetchLabel(type.getModule()); + return entry; +} } // namespace codeql diff --git a/swift/extractor/visitors/TypeVisitor.h b/swift/extractor/visitors/TypeVisitor.h index 38e9e71b32f..21cbd96d24d 100644 --- a/swift/extractor/visitors/TypeVisitor.h +++ b/swift/extractor/visitors/TypeVisitor.h @@ -71,6 +71,7 @@ class TypeVisitor : public TypeVisitorBase { const swift::BuiltinUnsafeValueBufferType& type); codeql::BuiltinVectorType translateBuiltinVectorType(const swift::BuiltinVectorType& type); codeql::OpenedArchetypeType translateOpenedArchetypeType(const swift::OpenedArchetypeType& type); + codeql::ModuleType translateModuleType(const swift::ModuleType& type); private: void fillType(const swift::TypeBase& type, codeql::Type& entry); @@ -83,9 +84,9 @@ class TypeVisitor : public TypeVisitorBase { void emitBoundGenericType(swift::BoundGenericType* type, TrapLabel label); void emitAnyGenericType(swift::AnyGenericType* type, TrapLabel label); - template - auto createTypeEntry(const T& type) { - auto entry = dispatcher_.createEntry(type); + template + auto createTypeEntry(const T& type, const Args&... args) { + auto entry = dispatcher_.createEntry(type, args...); fillType(type, entry); return entry; } diff --git a/swift/ql/lib/codeql/swift/generated/type/ModuleType.qll b/swift/ql/lib/codeql/swift/generated/type/ModuleType.qll index 91da39854d7..866598ad431 100644 --- a/swift/ql/lib/codeql/swift/generated/type/ModuleType.qll +++ b/swift/ql/lib/codeql/swift/generated/type/ModuleType.qll @@ -1,6 +1,14 @@ // generated by codegen/codegen.py +import codeql.swift.elements.decl.ModuleDecl import codeql.swift.elements.type.Type class ModuleTypeBase extends @module_type, Type { override string getAPrimaryQlClass() { result = "ModuleType" } + + ModuleDecl getModule() { + exists(ModuleDecl x | + module_types(this, x) and + result = x.resolve() + ) + } } diff --git a/swift/ql/lib/swift.dbscheme b/swift/ql/lib/swift.dbscheme index 0826b2b9ef4..7c102d30524 100644 --- a/swift/ql/lib/swift.dbscheme +++ b/swift/ql/lib/swift.dbscheme @@ -264,7 +264,8 @@ l_value_types( //dir=type ); module_types( //dir=type - unique int id: @module_type + unique int id: @module_type, + int module: @module_decl ref ); placeholder_types( //dir=type diff --git a/swift/ql/test/TestUtils.qll b/swift/ql/test/TestUtils.qll index 0c04fcb5989..265513b4f4a 100644 --- a/swift/ql/test/TestUtils.qll +++ b/swift/ql/test/TestUtils.qll @@ -4,7 +4,11 @@ cached predicate toBeTested(Element e) { e instanceof File or - exists(ModuleDecl m | m = e and not m.isBuiltinModule() and not m.isSystemModule()) + exists(ModuleDecl m | + not m.isBuiltinModule() and + not m.isSystemModule() and + (m = e or m.getInterfaceType() = e) + ) or exists(Locatable loc | loc.getLocation().getFile().getName().matches("%swift/ql/test%") and diff --git a/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/ModuleDecl.expected b/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/ModuleDecl.expected index ef50edbe828..cfae187857a 100644 --- a/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/ModuleDecl.expected +++ b/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/ModuleDecl.expected @@ -1 +1,2 @@ | file://:0:0:0:0 | Foo | getInterfaceType: | module | getName: | Foo | isBuiltinModule: | no | isSystemModule: | no | +| file://:0:0:0:0 | default_module_name | getInterfaceType: | module | getName: | default_module_name | isBuiltinModule: | no | isSystemModule: | no | diff --git a/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/default_module_name.swift b/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/default_module_name.swift new file mode 100644 index 00000000000..e69de29bb2d diff --git a/swift/ql/test/extractor-tests/generated/type/ModuleType/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/type/ModuleType/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/type/ModuleType/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/type/ModuleType/ModuleType.expected b/swift/ql/test/extractor-tests/generated/type/ModuleType/ModuleType.expected new file mode 100644 index 00000000000..3a688c7fbd6 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/type/ModuleType/ModuleType.expected @@ -0,0 +1,2 @@ +| module | getName: | module | getCanonicalType: | module | getModule: | file://:0:0:0:0 | Foo | +| module | getName: | module | getCanonicalType: | module | getModule: | file://:0:0:0:0 | default_module_name | diff --git a/swift/ql/test/extractor-tests/generated/type/ModuleType/ModuleType.ql b/swift/ql/test/extractor-tests/generated/type/ModuleType/ModuleType.ql new file mode 100644 index 00000000000..aa070b52e34 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/type/ModuleType/ModuleType.ql @@ -0,0 +1,12 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from ModuleType x, string getName, Type getCanonicalType, ModuleDecl getModule +where + toBeTested(x) and + not x.isUnknown() and + getName = x.getName() and + getCanonicalType = x.getCanonicalType() and + getModule = x.getModule() +select x, "getName:", getName, "getCanonicalType:", getCanonicalType, "getModule:", getModule diff --git a/swift/ql/test/extractor-tests/generated/type/ModuleType/default_module_name.swift b/swift/ql/test/extractor-tests/generated/type/ModuleType/default_module_name.swift new file mode 100644 index 00000000000..e69de29bb2d diff --git a/swift/ql/test/extractor-tests/generated/type/ModuleType/modules.swift b/swift/ql/test/extractor-tests/generated/type/ModuleType/modules.swift new file mode 100644 index 00000000000..bb12ed63ddd --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/type/ModuleType/modules.swift @@ -0,0 +1 @@ +//codeql-extractor-options: -module-name Foo From 4ce100f9a38b0f319fdf642d8c28051d4042226b Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Fri, 29 Jul 2022 16:51:49 +0200 Subject: [PATCH 382/505] Swift: append clang module names in trap keys We have found out there can be separate declarations (`VarDecl` or `AccessorDecl`) which are effectively the same (with equal mangled name) but come from different clang modules. This is the case for example for glibc constants like `L_SET` that appear in both `SwiftGlibc` and `CDispatch`. In this patch, we simply avoid full deduplication in that case by appending the module name to the trap key for non-swift modules. A more solid solution should be found in the future. --- swift/extractor/visitors/DeclVisitor.cpp | 31 ++++++++++++++++-------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/swift/extractor/visitors/DeclVisitor.cpp b/swift/extractor/visitors/DeclVisitor.cpp index 2b3775171e9..fce28e504dd 100644 --- a/swift/extractor/visitors/DeclVisitor.cpp +++ b/swift/extractor/visitors/DeclVisitor.cpp @@ -295,19 +295,30 @@ std::string DeclVisitor::mangledName(const swift::ValueDecl& decl) { // ASTMangler::mangleAnyDecl crashes when called on `ModuleDecl` // TODO find a more unique string working also when different modules are compiled with the same // name + std::ostringstream ret; if (decl.getKind() == swift::DeclKind::Module) { - return static_cast(decl).getRealName().str().str(); + ret << static_cast(decl).getRealName().str().str(); + } else if (decl.getKind() == swift::DeclKind::TypeAlias) { + // In cases like this (when coming from PCM) + // typealias CFXMLTree = CFTree + // typealias CFXMLTreeRef = CFXMLTree + // mangleAnyDecl mangles both CFXMLTree and CFXMLTreeRef into 'So12CFXMLTreeRefa' + // which is not correct and causes inconsistencies. mangleEntity makes these two distinct + // prefix adds a couple of special symbols, we don't necessary need them + ret << mangler.mangleEntity(&decl); + } else { + // prefix adds a couple of special symbols, we don't necessary need them + ret << mangler.mangleAnyDecl(&decl, /* prefix = */ false); } - // In cases like this (when coming from PCM) - // typealias CFXMLTree = CFTree - // typealias CFXMLTreeRef = CFXMLTree - // mangleAnyDecl mangles both CFXMLTree and CFXMLTreeRef into 'So12CFXMLTreeRefa' - // which is not correct and causes inconsistencies. mangleEntity makes these two distinct - if (decl.getKind() == swift::DeclKind::TypeAlias) { - return mangler.mangleEntity(&decl); + // there can be separate declarations (`VarDecl` or `AccessorDecl`) which are effectively the same + // (with equal mangled name) but come from different clang modules. This is the case for example + // for glibc constants like `L_SET` that appear in both `SwiftGlibc` and `CDispatch`. + // For the moment, we sidestep the problem by making them separate entities in the DB + // TODO find a more solid solution + if (decl.getModuleContext()->isNonSwiftModule()) { + ret << '_' << decl.getModuleContext()->getRealName().str().str(); } - // prefix adds a couple of special symbols, we don't necessary need them - return mangler.mangleAnyDecl(&decl, /* prefix = */ false); + return ret.str(); } void DeclVisitor::fillAbstractFunctionDecl(const swift::AbstractFunctionDecl& decl, From 98a9cb0b559c0c3ed98321e5e73ab931fcdc17c9 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 29 Jul 2022 19:44:10 +0200 Subject: [PATCH 383/505] JS: Simplify type hierarchy for SourceNode The charpred caused spurious type to appear --- .../ql/lib/semmle/javascript/dataflow/Sources.qll | 14 +------------- .../RecursionPrevention/SourceNodeRange.expected | 1 - .../RecursionPrevention/SourceNodeRange.ql | 14 -------------- 3 files changed, 1 insertion(+), 28 deletions(-) delete mode 100644 javascript/ql/test/library-tests/RecursionPrevention/SourceNodeRange.expected delete mode 100644 javascript/ql/test/library-tests/RecursionPrevention/SourceNodeRange.ql diff --git a/javascript/ql/lib/semmle/javascript/dataflow/Sources.qll b/javascript/ql/lib/semmle/javascript/dataflow/Sources.qll index 73d3f9c96ac..8813b974add 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/Sources.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/Sources.qll @@ -33,13 +33,7 @@ private import semmle.javascript.internal.CachedStages * import("fs") * ``` */ -class SourceNode extends DataFlow::Node { - SourceNode() { - this instanceof SourceNode::Range - or - none() and this instanceof SourceNode::Internal::RecursionGuard - } - +class SourceNode extends DataFlow::Node instanceof SourceNode::Range { /** * Holds if this node flows into `sink` in zero or more local (that is, * intra-procedural) steps. @@ -340,12 +334,6 @@ module SourceNode { DataFlow::functionReturnNode(this, _) } } - - /** INTERNAL. DO NOT USE. */ - module Internal { - /** An empty class that some tests are using to enforce that SourceNode is non-recursive. */ - abstract class RecursionGuard extends DataFlow::Node { } - } } private class NodeModuleSourcesNodes extends SourceNode::Range { diff --git a/javascript/ql/test/library-tests/RecursionPrevention/SourceNodeRange.expected b/javascript/ql/test/library-tests/RecursionPrevention/SourceNodeRange.expected deleted file mode 100644 index 59f6fd6e79b..00000000000 --- a/javascript/ql/test/library-tests/RecursionPrevention/SourceNodeRange.expected +++ /dev/null @@ -1 +0,0 @@ -| Success | diff --git a/javascript/ql/test/library-tests/RecursionPrevention/SourceNodeRange.ql b/javascript/ql/test/library-tests/RecursionPrevention/SourceNodeRange.ql deleted file mode 100644 index 03549a97873..00000000000 --- a/javascript/ql/test/library-tests/RecursionPrevention/SourceNodeRange.ql +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Test that fails to compile if the domain of `SourceNode::Range` depends on `SourceNode` (recursively). - * - * This tests adds a negative dependency `SourceNode --!--> SourceNode::Range` - * so that the undesired edge `SourceNode::Range --> SourceNode` completes a negative cycle. - */ - -import javascript - -class BadSourceNodeRange extends DataFlow::SourceNode::Internal::RecursionGuard { - BadSourceNodeRange() { not this instanceof DataFlow::SourceNode::Range } -} - -select "Success" From c02e7a4896629610bd5480ca0b3fc0407b95b58c Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Sun, 31 Jul 2022 09:58:29 +0200 Subject: [PATCH 384/505] C++: Update test for indexing of static template variable template arguments --- cpp/ql/test/library-tests/templates/CPP-203/decls.expected | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp/ql/test/library-tests/templates/CPP-203/decls.expected b/cpp/ql/test/library-tests/templates/CPP-203/decls.expected index b311041021d..33aa6114052 100644 --- a/cpp/ql/test/library-tests/templates/CPP-203/decls.expected +++ b/cpp/ql/test/library-tests/templates/CPP-203/decls.expected @@ -15,6 +15,7 @@ | test.cpp:3:8:3:8 | operator= | | test.cpp:3:8:3:10 | Str | | test.cpp:3:8:3:10 | Str | +| test.cpp:7:16:7:16 | T | | test.cpp:8:11:8:21 | val | | test.cpp:8:19:8:19 | val | | test.cpp:10:6:10:6 | f | From 96e220588ebcf07635df88692f09f7746892087d Mon Sep 17 00:00:00 2001 From: ihsinme Date: Sun, 31 Jul 2022 13:44:50 +0300 Subject: [PATCH 385/505] Update DangerousUseMbtowc.ql --- .../src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.ql b/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.ql index 6c0bf84e81b..0b87d920b6f 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.ql @@ -117,7 +117,7 @@ predicate findUseCharacterConversion(Expr exp, string msg) { predicate findUseMultibyteCharacter(Expr exp, string msg) { exists(ArrayType arrayType, ArrayExpr arrayExpr | arrayExpr = exp and - arrayExpr.getArrayBase().(VariableAccess).getTarget().getADeclarationEntry().getType() = + arrayExpr.getArrayBase().getType() = arrayType and ( exists(AssignExpr assZero, SizeofExprOperator sizeofArray, Expr oneValue | From bc05cdaa4d916362d9de5ff3965ead70ea1207c7 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Mon, 1 Aug 2022 11:56:51 +0200 Subject: [PATCH 386/505] Implement fetch-codeql using 'gh codeql' --- .github/actions/fetch-codeql/action.yml | 18 ++++-------------- .github/workflows/check-qldoc.yml | 15 +++++---------- 2 files changed, 9 insertions(+), 24 deletions(-) diff --git a/.github/actions/fetch-codeql/action.yml b/.github/actions/fetch-codeql/action.yml index 13b91525237..d1f48f40047 100644 --- a/.github/actions/fetch-codeql/action.yml +++ b/.github/actions/fetch-codeql/action.yml @@ -3,22 +3,12 @@ description: Fetches the latest version of CodeQL runs: using: composite steps: - - name: Select platform - Linux - if: runner.os == 'Linux' - shell: bash - run: echo "GA_CODEQL_CLI_PLATFORM=linux64" >> $GITHUB_ENV - - - name: Select platform - MacOS - if: runner.os == 'MacOS' - shell: bash - run: echo "GA_CODEQL_CLI_PLATFORM=osx64" >> $GITHUB_ENV - - name: Fetch CodeQL shell: bash run: | - LATEST=$(gh release list --repo https://github.com/github/codeql-cli-binaries | cut -f 1 | grep -v beta | sort --version-sort | tail -1) - gh release download --repo https://github.com/github/codeql-cli-binaries --pattern codeql-$GA_CODEQL_CLI_PLATFORM.zip "$LATEST" - unzip -q -d "${RUNNER_TEMP}" codeql-$GA_CODEQL_CLI_PLATFORM.zip - echo "${RUNNER_TEMP}/codeql" >> "${GITHUB_PATH}" + gh extension install github/gh-codeql + gh codeql set-channel release + gh codeql version + gh codeql version --format=json | jq -r .unpackedLocation >> "${GITHUB_PATH}" env: GITHUB_TOKEN: ${{ github.token }} diff --git a/.github/workflows/check-qldoc.yml b/.github/workflows/check-qldoc.yml index 77f524b73e7..f0256b1758a 100644 --- a/.github/workflows/check-qldoc.yml +++ b/.github/workflows/check-qldoc.yml @@ -14,18 +14,13 @@ jobs: runs-on: ubuntu-latest steps: - - name: Install CodeQL - run: | - gh extension install github/gh-codeql - gh codeql set-channel nightly - gh codeql version - env: - GITHUB_TOKEN: ${{ github.token }} - - uses: actions/checkout@v3 with: fetch-depth: 2 + - name: Install CodeQL + uses: ./.github/actions/fetch-codeql + - name: Check QLdoc coverage shell: bash run: | @@ -34,7 +29,7 @@ jobs: changed_lib_packs="$(git diff --name-only --diff-filter=ACMRT HEAD^ HEAD | { grep -Po '^(?!swift)[a-z]*/ql/lib' || true; } | sort -u)" for pack_dir in ${changed_lib_packs}; do lang="${pack_dir%/ql/lib}" - gh codeql generate library-doc-coverage --output="${RUNNER_TEMP}/${lang}-current.txt" --dir="${pack_dir}" + codeql generate library-doc-coverage --output="${RUNNER_TEMP}/${lang}-current.txt" --dir="${pack_dir}" done git checkout HEAD^ for pack_dir in ${changed_lib_packs}; do @@ -42,7 +37,7 @@ jobs: # In this case the right thing to do is to skip the check. [[ ! -d "${pack_dir}" ]] && continue lang="${pack_dir%/ql/lib}" - gh codeql generate library-doc-coverage --output="${RUNNER_TEMP}/${lang}-baseline.txt" --dir="${pack_dir}" + codeql generate library-doc-coverage --output="${RUNNER_TEMP}/${lang}-baseline.txt" --dir="${pack_dir}" awk -F, '{gsub(/"/,""); if ($4==0 && $6=="public") print "\""$3"\"" }' "${RUNNER_TEMP}/${lang}-current.txt" | sort -u > "${RUNNER_TEMP}/current-undocumented.txt" awk -F, '{gsub(/"/,""); if ($4==0 && $6=="public") print "\""$3"\"" }' "${RUNNER_TEMP}/${lang}-baseline.txt" | sort -u > "${RUNNER_TEMP}/baseline-undocumented.txt" UNDOCUMENTED="$(grep -f <(comm -13 "${RUNNER_TEMP}/baseline-undocumented.txt" "${RUNNER_TEMP}/current-undocumented.txt") "${RUNNER_TEMP}/${lang}-current.txt" || true)" From 3b8eeb09bf4a3521c6022768e71bf4cc3b5d5544 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Mon, 1 Aug 2022 12:16:15 +0200 Subject: [PATCH 387/505] Add fetch-codeql path to Actions triggers --- .github/workflows/check-qldoc.yml | 1 + .github/workflows/csv-coverage-metrics.yml | 1 + .github/workflows/js-ml-tests.yml | 2 ++ .github/workflows/mad_regenerate-models.yml | 1 + .github/workflows/query-list.yml | 1 + .github/workflows/ruby-qltest.yml | 2 ++ .github/workflows/swift-codegen.yml | 1 + .github/workflows/swift-integration-tests.yml | 1 + .github/workflows/swift-qltest.yml | 1 + .github/workflows/validate-change-notes.yml | 2 ++ 10 files changed, 13 insertions(+) diff --git a/.github/workflows/check-qldoc.yml b/.github/workflows/check-qldoc.yml index f0256b1758a..be986d5ecf6 100644 --- a/.github/workflows/check-qldoc.yml +++ b/.github/workflows/check-qldoc.yml @@ -5,6 +5,7 @@ on: paths: - "*/ql/lib/**" - .github/workflows/check-qldoc.yml + - .github/actions/fetch-codeql branches: - main - "rc/*" diff --git a/.github/workflows/csv-coverage-metrics.yml b/.github/workflows/csv-coverage-metrics.yml index 7778221dc2f..e263572398e 100644 --- a/.github/workflows/csv-coverage-metrics.yml +++ b/.github/workflows/csv-coverage-metrics.yml @@ -12,6 +12,7 @@ on: - main paths: - ".github/workflows/csv-coverage-metrics.yml" + - ".github/actions/fetch-codeql" jobs: publish-java: diff --git a/.github/workflows/js-ml-tests.yml b/.github/workflows/js-ml-tests.yml index 65db215d8c3..0b23f91ed48 100644 --- a/.github/workflows/js-ml-tests.yml +++ b/.github/workflows/js-ml-tests.yml @@ -5,6 +5,7 @@ on: paths: - "javascript/ql/experimental/adaptivethreatmodeling/**" - .github/workflows/js-ml-tests.yml + - .github/actions/fetch-codeql - codeql-workspace.yml branches: - main @@ -13,6 +14,7 @@ on: paths: - "javascript/ql/experimental/adaptivethreatmodeling/**" - .github/workflows/js-ml-tests.yml + - .github/actions/fetch-codeql - codeql-workspace.yml workflow_dispatch: diff --git a/.github/workflows/mad_regenerate-models.yml b/.github/workflows/mad_regenerate-models.yml index d1d7e6e3791..9f16c223ec6 100644 --- a/.github/workflows/mad_regenerate-models.yml +++ b/.github/workflows/mad_regenerate-models.yml @@ -9,6 +9,7 @@ on: - main paths: - ".github/workflows/mad_regenerate-models.yml" + - ".github/actions/fetch-codeql" jobs: regenerate-models: diff --git a/.github/workflows/query-list.yml b/.github/workflows/query-list.yml index 9416b740c99..56604b17cdc 100644 --- a/.github/workflows/query-list.yml +++ b/.github/workflows/query-list.yml @@ -10,6 +10,7 @@ on: pull_request: paths: - '.github/workflows/query-list.yml' + - '.github/actions/fetch-codeql' - 'misc/scripts/generate-code-scanning-query-list.py' jobs: diff --git a/.github/workflows/ruby-qltest.yml b/.github/workflows/ruby-qltest.yml index 0cf8860d8f1..e5eb7e05ecd 100644 --- a/.github/workflows/ruby-qltest.yml +++ b/.github/workflows/ruby-qltest.yml @@ -5,6 +5,7 @@ on: paths: - "ruby/**" - .github/workflows/ruby-qltest.yml + - .github/actions/fetch-codeql - codeql-workspace.yml branches: - main @@ -13,6 +14,7 @@ on: paths: - "ruby/**" - .github/workflows/ruby-qltest.yml + - .github/actions/fetch-codeql - codeql-workspace.yml branches: - main diff --git a/.github/workflows/swift-codegen.yml b/.github/workflows/swift-codegen.yml index 46a27709717..665ee55a247 100644 --- a/.github/workflows/swift-codegen.yml +++ b/.github/workflows/swift-codegen.yml @@ -5,6 +5,7 @@ on: paths: - "swift/**" - .github/workflows/swift-codegen.yml + - .github/actions/fetch-codeql branches: - main diff --git a/.github/workflows/swift-integration-tests.yml b/.github/workflows/swift-integration-tests.yml index 591ea2b12f7..cc365809c73 100644 --- a/.github/workflows/swift-integration-tests.yml +++ b/.github/workflows/swift-integration-tests.yml @@ -5,6 +5,7 @@ on: paths: - "swift/**" - .github/workflows/swift-integration-tests.yml + - .github/actions/fetch-codeql - codeql-workspace.yml branches: - main diff --git a/.github/workflows/swift-qltest.yml b/.github/workflows/swift-qltest.yml index 915e1f331a5..76a21b0bd8a 100644 --- a/.github/workflows/swift-qltest.yml +++ b/.github/workflows/swift-qltest.yml @@ -5,6 +5,7 @@ on: paths: - "swift/**" - .github/workflows/swift-qltest.yml + - .github/actions/fetch-codeql - codeql-workspace.yml branches: - main diff --git a/.github/workflows/validate-change-notes.yml b/.github/workflows/validate-change-notes.yml index 798913746be..b06167ea905 100644 --- a/.github/workflows/validate-change-notes.yml +++ b/.github/workflows/validate-change-notes.yml @@ -5,6 +5,7 @@ on: paths: - "*/ql/*/change-notes/**/*" - ".github/workflows/validate-change-notes.yml" + - ".github/actions/fetch-codeql" branches: - main - "rc/*" @@ -12,6 +13,7 @@ on: paths: - "*/ql/*/change-notes/**/*" - ".github/workflows/validate-change-notes.yml" + - ".github/actions/fetch-codeql" jobs: check-change-note: From 2bbd2f36c9e070594a75b0f9475b987537bb0ded Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Mon, 1 Aug 2022 12:26:03 +0200 Subject: [PATCH 388/505] Fix .github/workflows/query-list.yml --- .github/workflows/query-list.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/query-list.yml b/.github/workflows/query-list.yml index 56604b17cdc..0cf1cf30422 100644 --- a/.github/workflows/query-list.yml +++ b/.github/workflows/query-list.yml @@ -30,8 +30,6 @@ jobs: - name: Download CodeQL CLI # Look under the `codeql` directory, as this is where we checked out the `github/codeql` repo uses: ./codeql/.github/actions/fetch-codeql - - name: Unzip CodeQL CLI - run: unzip -d codeql-cli codeql-linux64.zip - name: Build code scanning query list run: | python codeql/misc/scripts/generate-code-scanning-query-list.py > code-scanning-query-list.csv From 29381dc26413c30be85985e30f358e60ce1568c6 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Mon, 1 Aug 2022 12:38:59 +0200 Subject: [PATCH 389/505] Use fetch-codeql in more places --- .../workflows/csv-coverage-pr-artifacts.yml | 163 +++++++------- .github/workflows/csv-coverage-timeseries.yml | 57 +++-- .github/workflows/csv-coverage-update.yml | 52 ++--- .github/workflows/csv-coverage.yml | 65 +++--- .github/workflows/go-tests.yml | 198 +++++++----------- .github/workflows/ruby-build.yml | 31 +-- 6 files changed, 242 insertions(+), 324 deletions(-) diff --git a/.github/workflows/csv-coverage-pr-artifacts.yml b/.github/workflows/csv-coverage-pr-artifacts.yml index 379b3c5aad8..b63d85534b4 100644 --- a/.github/workflows/csv-coverage-pr-artifacts.yml +++ b/.github/workflows/csv-coverage-pr-artifacts.yml @@ -3,18 +3,20 @@ name: Check framework coverage changes on: pull_request: paths: - - '.github/workflows/csv-coverage-pr-comment.yml' - - '*/ql/src/**/*.ql' - - '*/ql/src/**/*.qll' - - '*/ql/lib/**/*.ql' - - '*/ql/lib/**/*.qll' - - 'misc/scripts/library-coverage/*.py' + - ".github/workflows/csv-coverage-pr-comment.yml" + - ".github/workflows/csv-coverage-pr-artifacts.yml" + - ".github/actions/fetch-codeql" + - "*/ql/src/**/*.ql" + - "*/ql/src/**/*.qll" + - "*/ql/lib/**/*.ql" + - "*/ql/lib/**/*.qll" + - "misc/scripts/library-coverage/*.py" # input data files - - '*/documentation/library-coverage/cwe-sink.csv' - - '*/documentation/library-coverage/frameworks.csv' + - "*/documentation/library-coverage/cwe-sink.csv" + - "*/documentation/library-coverage/frameworks.csv" branches: - main - - 'rc/*' + - "rc/*" jobs: generate: @@ -23,77 +25,72 @@ jobs: runs-on: ubuntu-latest steps: - - name: Dump GitHub context - env: - GITHUB_CONTEXT: ${{ toJSON(github.event) }} - run: echo "$GITHUB_CONTEXT" - - name: Clone self (github/codeql) - MERGE - uses: actions/checkout@v3 - with: - path: merge - - name: Clone self (github/codeql) - BASE - uses: actions/checkout@v3 - with: - fetch-depth: 2 - path: base - - run: | - git checkout HEAD^1 - git log -1 --format='%H' - working-directory: base - - name: Set up Python 3.8 - uses: actions/setup-python@v4 - with: - python-version: 3.8 - - name: Download CodeQL CLI - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - gh release download --repo "github/codeql-cli-binaries" --pattern "codeql-linux64.zip" - - name: Unzip CodeQL CLI - run: unzip -d codeql-cli codeql-linux64.zip - - name: Generate CSV files on merge commit of the PR - run: | - echo "Running generator on merge" - PATH="$PATH:codeql-cli/codeql" python merge/misc/scripts/library-coverage/generate-report.py ci merge merge - mkdir out_merge - cp framework-coverage-*.csv out_merge/ - cp framework-coverage-*.rst out_merge/ - - name: Generate CSV files on base commit of the PR - run: | - echo "Running generator on base" - PATH="$PATH:codeql-cli/codeql" python base/misc/scripts/library-coverage/generate-report.py ci base base - mkdir out_base - cp framework-coverage-*.csv out_base/ - cp framework-coverage-*.rst out_base/ - - name: Generate diff of coverage reports - run: | - python base/misc/scripts/library-coverage/compare-folders.py out_base out_merge comparison.md - - name: Upload CSV package list - uses: actions/upload-artifact@v3 - with: - name: csv-framework-coverage-merge - path: | - out_merge/framework-coverage-*.csv - out_merge/framework-coverage-*.rst - - name: Upload CSV package list - uses: actions/upload-artifact@v3 - with: - name: csv-framework-coverage-base - path: | - out_base/framework-coverage-*.csv - out_base/framework-coverage-*.rst - - name: Upload comparison results - uses: actions/upload-artifact@v3 - with: - name: comparison - path: | - comparison.md - - name: Save PR number - run: | - mkdir -p pr - echo ${{ github.event.pull_request.number }} > pr/NR - - name: Upload PR number - uses: actions/upload-artifact@v3 - with: - name: pr - path: pr/ + - name: Dump GitHub context + env: + GITHUB_CONTEXT: ${{ toJSON(github.event) }} + run: echo "$GITHUB_CONTEXT" + - name: Clone self (github/codeql) - MERGE + uses: actions/checkout@v3 + with: + path: merge + - name: Clone self (github/codeql) - BASE + uses: actions/checkout@v3 + with: + fetch-depth: 2 + path: base + - run: | + git checkout HEAD^1 + git log -1 --format='%H' + working-directory: base + - name: Set up Python 3.8 + uses: actions/setup-python@v4 + with: + python-version: 3.8 + - name: Download CodeQL CLI + uses: ./merge/.github/actions/fetch-codeql + - name: Generate CSV files on merge commit of the PR + run: | + echo "Running generator on merge" + PATH="$PATH:codeql-cli/codeql" python merge/misc/scripts/library-coverage/generate-report.py ci merge merge + mkdir out_merge + cp framework-coverage-*.csv out_merge/ + cp framework-coverage-*.rst out_merge/ + - name: Generate CSV files on base commit of the PR + run: | + echo "Running generator on base" + PATH="$PATH:codeql-cli/codeql" python base/misc/scripts/library-coverage/generate-report.py ci base base + mkdir out_base + cp framework-coverage-*.csv out_base/ + cp framework-coverage-*.rst out_base/ + - name: Generate diff of coverage reports + run: | + python base/misc/scripts/library-coverage/compare-folders.py out_base out_merge comparison.md + - name: Upload CSV package list + uses: actions/upload-artifact@v3 + with: + name: csv-framework-coverage-merge + path: | + out_merge/framework-coverage-*.csv + out_merge/framework-coverage-*.rst + - name: Upload CSV package list + uses: actions/upload-artifact@v3 + with: + name: csv-framework-coverage-base + path: | + out_base/framework-coverage-*.csv + out_base/framework-coverage-*.rst + - name: Upload comparison results + uses: actions/upload-artifact@v3 + with: + name: comparison + path: | + comparison.md + - name: Save PR number + run: | + mkdir -p pr + echo ${{ github.event.pull_request.number }} > pr/NR + - name: Upload PR number + uses: actions/upload-artifact@v3 + with: + name: pr + path: pr/ diff --git a/.github/workflows/csv-coverage-timeseries.yml b/.github/workflows/csv-coverage-timeseries.yml index 95b084ea215..2eb9d0cdf84 100644 --- a/.github/workflows/csv-coverage-timeseries.yml +++ b/.github/workflows/csv-coverage-timeseries.yml @@ -5,38 +5,31 @@ on: jobs: build: - runs-on: ubuntu-latest steps: - - name: Clone self (github/codeql) - uses: actions/checkout@v3 - with: - path: script - - name: Clone self (github/codeql) for analysis - uses: actions/checkout@v3 - with: - path: codeqlModels - fetch-depth: 0 - - name: Set up Python 3.8 - uses: actions/setup-python@v4 - with: - python-version: 3.8 - - name: Download CodeQL CLI - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - gh release download --repo "github/codeql-cli-binaries" --pattern "codeql-linux64.zip" - - name: Unzip CodeQL CLI - run: unzip -d codeql-cli codeql-linux64.zip - - name: Build modeled package list - run: | - CLI=$(realpath "codeql-cli/codeql") - echo $CLI - PATH="$PATH:$CLI" python script/misc/scripts/library-coverage/generate-timeseries.py codeqlModels - - name: Upload timeseries CSV - uses: actions/upload-artifact@v3 - with: - name: framework-coverage-timeseries - path: framework-coverage-timeseries-*.csv - + - name: Clone self (github/codeql) + uses: actions/checkout@v3 + with: + path: script + - name: Clone self (github/codeql) for analysis + uses: actions/checkout@v3 + with: + path: codeqlModels + fetch-depth: 0 + - name: Set up Python 3.8 + uses: actions/setup-python@v4 + with: + python-version: 3.8 + - name: Download CodeQL CLI + uses: ./.github/actions/fetch-codeql + - name: Build modeled package list + run: | + CLI=$(realpath "codeql-cli/codeql") + echo $CLI + PATH="$PATH:$CLI" python script/misc/scripts/library-coverage/generate-timeseries.py codeqlModels + - name: Upload timeseries CSV + uses: actions/upload-artifact@v3 + with: + name: framework-coverage-timeseries + path: framework-coverage-timeseries-*.csv diff --git a/.github/workflows/csv-coverage-update.yml b/.github/workflows/csv-coverage-update.yml index c57056b6de1..58e60cc363e 100644 --- a/.github/workflows/csv-coverage-update.yml +++ b/.github/workflows/csv-coverage-update.yml @@ -12,33 +12,27 @@ jobs: runs-on: ubuntu-latest steps: - - name: Dump GitHub context - env: - GITHUB_CONTEXT: ${{ toJSON(github.event) }} - run: echo "$GITHUB_CONTEXT" - - name: Clone self (github/codeql) - uses: actions/checkout@v3 - with: - path: ql - fetch-depth: 0 - - name: Set up Python 3.8 - uses: actions/setup-python@v4 - with: - python-version: 3.8 - - name: Download CodeQL CLI - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - gh release download --repo "github/codeql-cli-binaries" --pattern "codeql-linux64.zip" - - name: Unzip CodeQL CLI - run: unzip -d codeql-cli codeql-linux64.zip + - name: Dump GitHub context + env: + GITHUB_CONTEXT: ${{ toJSON(github.event) }} + run: echo "$GITHUB_CONTEXT" + - name: Clone self (github/codeql) + uses: actions/checkout@v3 + with: + path: ql + fetch-depth: 0 + - name: Set up Python 3.8 + uses: actions/setup-python@v4 + with: + python-version: 3.8 + - name: Download CodeQL CLI + uses: ./.github/actions/fetch-codeql + - name: Generate coverage files + run: | + PATH="$PATH:codeql-cli/codeql" python ql/misc/scripts/library-coverage/generate-report.py ci ql ql - - name: Generate coverage files - run: | - PATH="$PATH:codeql-cli/codeql" python ql/misc/scripts/library-coverage/generate-report.py ci ql ql - - - name: Create pull request with changes - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - python ql/misc/scripts/library-coverage/create-pr.py ql "$GITHUB_REPOSITORY" + - name: Create pull request with changes + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + python ql/misc/scripts/library-coverage/create-pr.py ql "$GITHUB_REPOSITORY" diff --git a/.github/workflows/csv-coverage.yml b/.github/workflows/csv-coverage.yml index 9a308d50265..dfce019451e 100644 --- a/.github/workflows/csv-coverage.yml +++ b/.github/workflows/csv-coverage.yml @@ -4,46 +4,39 @@ on: workflow_dispatch: inputs: qlModelShaOverride: - description: 'github/codeql repo SHA used for looking up the CSV models' + description: "github/codeql repo SHA used for looking up the CSV models" required: false jobs: build: - runs-on: ubuntu-latest steps: - - name: Clone self (github/codeql) - uses: actions/checkout@v3 - with: - path: script - - name: Clone self (github/codeql) for analysis - uses: actions/checkout@v3 - with: - path: codeqlModels - ref: ${{ github.event.inputs.qlModelShaOverride || github.ref }} - - name: Set up Python 3.8 - uses: actions/setup-python@v4 - with: - python-version: 3.8 - - name: Download CodeQL CLI - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - gh release download --repo "github/codeql-cli-binaries" --pattern "codeql-linux64.zip" - - name: Unzip CodeQL CLI - run: unzip -d codeql-cli codeql-linux64.zip - - name: Build modeled package list - run: | - PATH="$PATH:codeql-cli/codeql" python script/misc/scripts/library-coverage/generate-report.py ci codeqlModels script - - name: Upload CSV package list - uses: actions/upload-artifact@v3 - with: - name: framework-coverage-csv - path: framework-coverage-*.csv - - name: Upload RST package list - uses: actions/upload-artifact@v3 - with: - name: framework-coverage-rst - path: framework-coverage-*.rst - + - name: Clone self (github/codeql) + uses: actions/checkout@v3 + with: + path: script + - name: Clone self (github/codeql) for analysis + uses: actions/checkout@v3 + with: + path: codeqlModels + ref: ${{ github.event.inputs.qlModelShaOverride || github.ref }} + - name: Set up Python 3.8 + uses: actions/setup-python@v4 + with: + python-version: 3.8 + - name: Download CodeQL CLI + uses: ./.github/actions/fetch-codeql + - name: Build modeled package list + run: | + PATH="$PATH:codeql-cli/codeql" python script/misc/scripts/library-coverage/generate-report.py ci codeqlModels script + - name: Upload CSV package list + uses: actions/upload-artifact@v3 + with: + name: framework-coverage-csv + path: framework-coverage-*.csv + - name: Upload RST package list + uses: actions/upload-artifact@v3 + with: + name: framework-coverage-rst + path: framework-coverage-*.rst diff --git a/.github/workflows/go-tests.yml b/.github/workflows/go-tests.yml index ca126d1a3ee..6001a18aad1 100644 --- a/.github/workflows/go-tests.yml +++ b/.github/workflows/go-tests.yml @@ -4,159 +4,111 @@ on: paths: - "go/**" - .github/workflows/go-tests.yml + - .github/actions/fetch-codeql - codeql-workspace.yml jobs: - test-linux: name: Test Linux (Ubuntu) runs-on: ubuntu-latest steps: + - name: Set up Go 1.18.1 + uses: actions/setup-go@v3 + with: + go-version: 1.18.1 + id: go - - name: Set up Go 1.18.1 - uses: actions/setup-go@v3 - with: - go-version: 1.18.1 - id: go + - name: Check out code + uses: actions/checkout@v2 - - name: Set up CodeQL CLI - run: | - echo "Removing old CodeQL Directory..." - rm -rf $HOME/codeql - echo "Done" - cd $HOME - echo "Downloading CodeQL CLI..." - LATEST=$(gh release list --repo https://github.com/github/codeql-cli-binaries | cut -f 1 | sort --version-sort | grep -v beta | tail -1) - gh release download --repo https://github.com/github/codeql-cli-binaries --pattern codeql-linux64.zip "$LATEST" - echo "Done" - echo "Unpacking CodeQL CLI..." - unzip -q codeql-linux64.zip - rm -f codeql-linux64.zip - echo "Done" - env: - GITHUB_TOKEN: ${{ github.token }} + - name: Set up CodeQL CLI + uses: ./.github/actions/fetch-codeql - - name: Check out code - uses: actions/checkout@v2 + - name: Enable problem matchers in repository + shell: bash + run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;' - - name: Enable problem matchers in repository - shell: bash - run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;' + - name: Build + run: | + cd go + env make - - name: Build - run: | - cd go - env PATH=$PATH:$HOME/codeql make + - name: Check that all QL and Go code is autoformatted + run: | + cd go + env make check-formatting - - name: Check that all QL and Go code is autoformatted - run: | - cd go - env PATH=$PATH:$HOME/codeql make check-formatting + - name: Compile qhelp files to markdown + run: | + cd go + env QHELP_OUT_DIR=qhelp-out make qhelp-to-markdown - - name: Compile qhelp files to markdown - run: | - cd go - env PATH=$PATH:$HOME/codeql QHELP_OUT_DIR=qhelp-out make qhelp-to-markdown + - name: Upload qhelp markdown + uses: actions/upload-artifact@v2 + with: + name: qhelp-markdown + path: go/qhelp-out/**/*.md - - name: Upload qhelp markdown - uses: actions/upload-artifact@v2 - with: - name: qhelp-markdown - path: go/qhelp-out/**/*.md - - - name: Test - run: | - cd go - env PATH=$PATH:$HOME/codeql make test + - name: Test + run: | + cd go + env make test test-mac: name: Test MacOS - runs-on: macOS-latest + runs-on: macos-latest steps: - - name: Set up Go 1.18.1 - uses: actions/setup-go@v3 - with: - go-version: 1.18.1 - id: go + - name: Set up Go 1.18.1 + uses: actions/setup-go@v3 + with: + go-version: 1.18.1 + id: go - - name: Set up CodeQL CLI - run: | - echo "Removing old CodeQL Directory..." - rm -rf $HOME/codeql - echo "Done" - cd $HOME - echo "Downloading CodeQL CLI..." - LATEST=$(gh release list --repo https://github.com/github/codeql-cli-binaries | cut -f 1 | sort --version-sort | grep -v beta | tail -1) - gh release download --repo https://github.com/github/codeql-cli-binaries --pattern codeql-osx64.zip "$LATEST" - echo "Done" - echo "Unpacking CodeQL CLI..." - unzip -q codeql-osx64.zip - rm -f codeql-osx64.zip - echo "Done" - env: - GITHUB_TOKEN: ${{ github.token }} + - name: Check out code + uses: actions/checkout@v2 - - name: Check out code - uses: actions/checkout@v2 + - name: Set up CodeQL CLI + uses: ./.github/actions/fetch-codeql - - name: Enable problem matchers in repository - shell: bash - run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;' + - name: Enable problem matchers in repository + shell: bash + run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;' - - name: Build - run: | - cd go - env PATH=$PATH:$HOME/codeql make + - name: Build + run: | + cd go + make - - name: Test - run: | - cd go - env PATH=$PATH:$HOME/codeql make test + - name: Test + run: | + cd go + make test test-win: name: Test Windows runs-on: windows-2019 steps: - - name: Set up Go 1.18.1 - uses: actions/setup-go@v3 - with: - go-version: 1.18.1 - id: go + - name: Set up Go 1.18.1 + uses: actions/setup-go@v3 + with: + go-version: 1.18.1 + id: go - - name: Set up CodeQL CLI - run: | - echo "Removing old CodeQL Directory..." - rm -rf $HOME/codeql - echo "Done" - cd "$HOME" - echo "Downloading CodeQL CLI..." - LATEST=$(gh release list --repo https://github.com/github/codeql-cli-binaries | cut -f 1 | sort --version-sort | grep -v beta | tail -1) - gh release download --repo https://github.com/github/codeql-cli-binaries --pattern codeql-win64.zip "$LATEST" - echo "Done" - echo "Unpacking CodeQL CLI..." - unzip -q -o codeql-win64.zip - unzip -q -o codeql-win64.zip codeql/codeql.exe - rm -f codeql-win64.zip - echo "Done" - env: - GITHUB_TOKEN: ${{ github.token }} - shell: - bash + - name: Check out code + uses: actions/checkout@v2 - - name: Check out code - uses: actions/checkout@v2 + - name: Set up CodeQL CLI + uses: ./.github/actions/fetch-codeql - - name: Enable problem matchers in repository - shell: bash - run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;' + - name: Enable problem matchers in repository + shell: bash + run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;' - - name: Build - run: | - $Env:Path += ";$HOME\codeql" - cd go - make + - name: Build + run: | + cd go + make - - name: Test - run: | - $Env:Path += ";$HOME\codeql" - cd go - make test + - name: Test + run: | + cd go + make test diff --git a/.github/workflows/ruby-build.yml b/.github/workflows/ruby-build.yml index c402312db0e..0322408e58f 100644 --- a/.github/workflows/ruby-build.yml +++ b/.github/workflows/ruby-build.yml @@ -90,19 +90,14 @@ jobs: steps: - uses: actions/checkout@v3 - name: Fetch CodeQL - run: | - LATEST=$(gh release list --repo https://github.com/github/codeql-cli-binaries | cut -f 1 | grep -v beta | sort --version-sort | tail -1) - gh release download --repo https://github.com/github/codeql-cli-binaries --pattern codeql-linux64.zip "$LATEST" - unzip -q codeql-linux64.zip - env: - GITHUB_TOKEN: ${{ github.token }} + uses: ./.github/actions/fetch-codeql - name: Build Query Pack run: | - codeql/codeql pack create ql/lib --output target/packs - codeql/codeql pack install ql/src - codeql/codeql pack create ql/src --output target/packs + codeql pack create ql/lib --output target/packs + codeql pack install ql/src + codeql pack create ql/src --output target/packs PACK_FOLDER=$(readlink -f target/packs/codeql/ruby-queries/*) - codeql/codeql generate query-help --format=sarifv2.1.0 --output="${PACK_FOLDER}/rules.sarif" ql/src + codeql generate query-help --format=sarifv2.1.0 --output="${PACK_FOLDER}/rules.sarif" ql/src (cd ql/src; find queries \( -name '*.qhelp' -o -name '*.rb' -o -name '*.erb' \) -exec bash -c 'mkdir -p "'"${PACK_FOLDER}"'/$(dirname "{}")"' \; -exec cp "{}" "${PACK_FOLDER}/{}" \;) - uses: actions/upload-artifact@v3 with: @@ -184,14 +179,8 @@ jobs: repository: Shopify/example-ruby-app ref: 67a0decc5eb550f3a9228eda53925c3afd40dfe9 - name: Fetch CodeQL - shell: bash - run: | - LATEST=$(gh release list --repo https://github.com/github/codeql-cli-binaries | cut -f 1 | grep -v beta | sort --version-sort | tail -1) - gh release download --repo https://github.com/github/codeql-cli-binaries --pattern codeql.zip "$LATEST" - unzip -q codeql.zip - env: - GITHUB_TOKEN: ${{ github.token }} - working-directory: ${{ runner.temp }} + uses: ./.github/actions/fetch-codeql + - name: Download Ruby bundle uses: actions/download-artifact@v3 with: @@ -215,12 +204,12 @@ jobs: - name: Run QL test shell: bash run: | - "${{ runner.temp }}/codeql/codeql" test run --search-path "${{ runner.temp }}/ruby-bundle" --additional-packs "${{ runner.temp }}/ruby-bundle" . + codeql test run --search-path "${{ runner.temp }}/ruby-bundle" --additional-packs "${{ runner.temp }}/ruby-bundle" . - name: Create database shell: bash run: | - "${{ runner.temp }}/codeql/codeql" database create --search-path "${{ runner.temp }}/ruby-bundle" --language ruby --source-root . ../database + codeql database create --search-path "${{ runner.temp }}/ruby-bundle" --language ruby --source-root . ../database - name: Analyze database shell: bash run: | - "${{ runner.temp }}/codeql/codeql" database analyze --search-path "${{ runner.temp }}/ruby-bundle" --format=sarifv2.1.0 --output=out.sarif ../database ruby-code-scanning.qls + codeql database analyze --search-path "${{ runner.temp }}/ruby-bundle" --format=sarifv2.1.0 --output=out.sarif ../database ruby-code-scanning.qls From 4d35d8da48fd03cda237c67ab162755b71080e83 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Mon, 1 Aug 2022 13:36:05 +0200 Subject: [PATCH 390/505] CI: fix Ruby build job --- .github/workflows/ruby-build.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ruby-build.yml b/.github/workflows/ruby-build.yml index 0322408e58f..2f7464e47b3 100644 --- a/.github/workflows/ruby-build.yml +++ b/.github/workflows/ruby-build.yml @@ -5,6 +5,7 @@ on: paths: - "ruby/**" - .github/workflows/ruby-build.yml + - .github/actions/fetch-codeql - codeql-workspace.yml branches: - main @@ -13,6 +14,7 @@ on: paths: - "ruby/**" - .github/workflows/ruby-build.yml + - .github/actions/fetch-codeql - codeql-workspace.yml branches: - main @@ -174,12 +176,14 @@ jobs: runs-on: ${{ matrix.os }} needs: [package] steps: + - uses: actions/checkout@v3 + - name: Fetch CodeQL + uses: ./.github/actions/fetch-codeql + - uses: actions/checkout@v3 with: repository: Shopify/example-ruby-app ref: 67a0decc5eb550f3a9228eda53925c3afd40dfe9 - - name: Fetch CodeQL - uses: ./.github/actions/fetch-codeql - name: Download Ruby bundle uses: actions/download-artifact@v3 From e29676af72944768b94913b22dc51261271c4f11 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Mon, 1 Aug 2022 16:48:02 +0100 Subject: [PATCH 391/505] Swift: Add 'TaintTracking.qll'. --- swift/ql/lib/codeql/swift/dataflow/TaintTracking.qll | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 swift/ql/lib/codeql/swift/dataflow/TaintTracking.qll diff --git a/swift/ql/lib/codeql/swift/dataflow/TaintTracking.qll b/swift/ql/lib/codeql/swift/dataflow/TaintTracking.qll new file mode 100644 index 00000000000..b554424211f --- /dev/null +++ b/swift/ql/lib/codeql/swift/dataflow/TaintTracking.qll @@ -0,0 +1,7 @@ +/** + * Provides classes for performing local (intra-procedural) and + * global (inter-procedural) taint-tracking analyses. + */ +module TaintTracking { + import codeql.swift.dataflow.internal.tainttracking1.TaintTrackingImpl +} From 7dc3d7d47ee32df1f77622c8b25aa7ba20bd9525 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Mon, 1 Aug 2022 18:15:05 +0200 Subject: [PATCH 392/505] CI: remove unneeded PATH definitions --- .github/workflows/csv-coverage-pr-artifacts.yml | 4 ++-- .github/workflows/csv-coverage-timeseries.yml | 4 +--- .github/workflows/csv-coverage-update.yml | 2 +- .github/workflows/csv-coverage.yml | 2 +- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/workflows/csv-coverage-pr-artifacts.yml b/.github/workflows/csv-coverage-pr-artifacts.yml index b63d85534b4..3649b99387b 100644 --- a/.github/workflows/csv-coverage-pr-artifacts.yml +++ b/.github/workflows/csv-coverage-pr-artifacts.yml @@ -51,14 +51,14 @@ jobs: - name: Generate CSV files on merge commit of the PR run: | echo "Running generator on merge" - PATH="$PATH:codeql-cli/codeql" python merge/misc/scripts/library-coverage/generate-report.py ci merge merge + python merge/misc/scripts/library-coverage/generate-report.py ci merge merge mkdir out_merge cp framework-coverage-*.csv out_merge/ cp framework-coverage-*.rst out_merge/ - name: Generate CSV files on base commit of the PR run: | echo "Running generator on base" - PATH="$PATH:codeql-cli/codeql" python base/misc/scripts/library-coverage/generate-report.py ci base base + python base/misc/scripts/library-coverage/generate-report.py ci base base mkdir out_base cp framework-coverage-*.csv out_base/ cp framework-coverage-*.rst out_base/ diff --git a/.github/workflows/csv-coverage-timeseries.yml b/.github/workflows/csv-coverage-timeseries.yml index 2eb9d0cdf84..ea216f68949 100644 --- a/.github/workflows/csv-coverage-timeseries.yml +++ b/.github/workflows/csv-coverage-timeseries.yml @@ -25,9 +25,7 @@ jobs: uses: ./.github/actions/fetch-codeql - name: Build modeled package list run: | - CLI=$(realpath "codeql-cli/codeql") - echo $CLI - PATH="$PATH:$CLI" python script/misc/scripts/library-coverage/generate-timeseries.py codeqlModels + python script/misc/scripts/library-coverage/generate-timeseries.py codeqlModels - name: Upload timeseries CSV uses: actions/upload-artifact@v3 with: diff --git a/.github/workflows/csv-coverage-update.yml b/.github/workflows/csv-coverage-update.yml index 58e60cc363e..1de2149ce2e 100644 --- a/.github/workflows/csv-coverage-update.yml +++ b/.github/workflows/csv-coverage-update.yml @@ -29,7 +29,7 @@ jobs: uses: ./.github/actions/fetch-codeql - name: Generate coverage files run: | - PATH="$PATH:codeql-cli/codeql" python ql/misc/scripts/library-coverage/generate-report.py ci ql ql + python ql/misc/scripts/library-coverage/generate-report.py ci ql ql - name: Create pull request with changes env: diff --git a/.github/workflows/csv-coverage.yml b/.github/workflows/csv-coverage.yml index dfce019451e..e829957a0d3 100644 --- a/.github/workflows/csv-coverage.yml +++ b/.github/workflows/csv-coverage.yml @@ -29,7 +29,7 @@ jobs: uses: ./.github/actions/fetch-codeql - name: Build modeled package list run: | - PATH="$PATH:codeql-cli/codeql" python script/misc/scripts/library-coverage/generate-report.py ci codeqlModels script + python script/misc/scripts/library-coverage/generate-report.py ci codeqlModels script - name: Upload CSV package list uses: actions/upload-artifact@v3 with: From e3cb7cf9fed80c2eade636208da9d14895275440 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Mon, 1 Aug 2022 17:30:23 +0100 Subject: [PATCH 393/505] C++: Remove internal 'microsoft' tags from queries. --- .../Likely Bugs/Likely Typos/IncorrectNotOperatorUsage.ql | 1 - cpp/ql/src/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean.ql | 3 +-- .../Likely Bugs/Likely Typos/inconsistentLoopDirection.ql | 1 - cpp/ql/src/Security/CWE/CWE-253/HResultBooleanConversion.ql | 5 ----- cpp/ql/src/Security/CWE/CWE-428/UnsafeCreateProcessCall.ql | 1 - cpp/ql/src/Security/CWE/CWE-704/WcharCharConversion.ql | 1 - .../src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.ql | 1 - 7 files changed, 1 insertion(+), 12 deletions(-) diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/IncorrectNotOperatorUsage.ql b/cpp/ql/src/Likely Bugs/Likely Typos/IncorrectNotOperatorUsage.ql index 30664869adc..9c0230d7514 100644 --- a/cpp/ql/src/Likely Bugs/Likely Typos/IncorrectNotOperatorUsage.ql +++ b/cpp/ql/src/Likely Bugs/Likely Typos/IncorrectNotOperatorUsage.ql @@ -10,7 +10,6 @@ * @precision medium * @tags security * external/cwe/cwe-480 - * external/microsoft/c6317 */ import cpp diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean.ql b/cpp/ql/src/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean.ql index 074c82bc03b..8770d249497 100644 --- a/cpp/ql/src/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean.ql +++ b/cpp/ql/src/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean.ql @@ -7,8 +7,7 @@ * @problem.severity error * @precision high * @id cpp/string-copy-return-value-as-boolean - * @tags external/microsoft/C6324 - * correctness + * @tags correctness */ import cpp diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/inconsistentLoopDirection.ql b/cpp/ql/src/Likely Bugs/Likely Typos/inconsistentLoopDirection.ql index 5e3af347821..9646d8b3adf 100644 --- a/cpp/ql/src/Likely Bugs/Likely Typos/inconsistentLoopDirection.ql +++ b/cpp/ql/src/Likely Bugs/Likely Typos/inconsistentLoopDirection.ql @@ -7,7 +7,6 @@ * @id cpp/inconsistent-loop-direction * @tags correctness * external/cwe/cwe-835 - * external/microsoft/6293 * @msrc.severity important */ diff --git a/cpp/ql/src/Security/CWE/CWE-253/HResultBooleanConversion.ql b/cpp/ql/src/Security/CWE/CWE-253/HResultBooleanConversion.ql index 67ba5b0c45b..eb746e2d1d2 100644 --- a/cpp/ql/src/Security/CWE/CWE-253/HResultBooleanConversion.ql +++ b/cpp/ql/src/Security/CWE/CWE-253/HResultBooleanConversion.ql @@ -8,11 +8,6 @@ * @precision high * @tags security * external/cwe/cwe-253 - * external/microsoft/C6214 - * external/microsoft/C6215 - * external/microsoft/C6216 - * external/microsoft/C6217 - * external/microsoft/C6230 */ import cpp diff --git a/cpp/ql/src/Security/CWE/CWE-428/UnsafeCreateProcessCall.ql b/cpp/ql/src/Security/CWE/CWE-428/UnsafeCreateProcessCall.ql index 7c540e9d313..ff8e85cecec 100644 --- a/cpp/ql/src/Security/CWE/CWE-428/UnsafeCreateProcessCall.ql +++ b/cpp/ql/src/Security/CWE/CWE-428/UnsafeCreateProcessCall.ql @@ -9,7 +9,6 @@ * @msrc.severity important * @tags security * external/cwe/cwe-428 - * external/microsoft/C6277 */ import cpp diff --git a/cpp/ql/src/Security/CWE/CWE-704/WcharCharConversion.ql b/cpp/ql/src/Security/CWE/CWE-704/WcharCharConversion.ql index 65551a1f138..aee4f3c8405 100644 --- a/cpp/ql/src/Security/CWE/CWE-704/WcharCharConversion.ql +++ b/cpp/ql/src/Security/CWE/CWE-704/WcharCharConversion.ql @@ -10,7 +10,6 @@ * @precision high * @tags security * external/cwe/cwe-704 - * external/microsoft/c/c6276 */ import cpp diff --git a/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.ql b/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.ql index 81998bda450..482b5daf992 100644 --- a/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.ql +++ b/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.ql @@ -11,7 +11,6 @@ * @precision high * @tags security * external/cwe/cwe-732 - * external/microsoft/C6248 */ import cpp From c63afbf7be990ea7ffd0fcf1577f0269ab54450d Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Mon, 1 Aug 2022 18:49:37 +0200 Subject: [PATCH 394/505] CI: remove left-over 'env' commands --- .github/workflows/go-tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/go-tests.yml b/.github/workflows/go-tests.yml index 6001a18aad1..14114ee2003 100644 --- a/.github/workflows/go-tests.yml +++ b/.github/workflows/go-tests.yml @@ -30,12 +30,12 @@ jobs: - name: Build run: | cd go - env make + make - name: Check that all QL and Go code is autoformatted run: | cd go - env make check-formatting + make check-formatting - name: Compile qhelp files to markdown run: | @@ -51,7 +51,7 @@ jobs: - name: Test run: | cd go - env make test + make test test-mac: name: Test MacOS From f0697ff28b7735bfc7944b06b5c335e66845a401 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Mon, 1 Aug 2022 15:23:59 -0400 Subject: [PATCH 395/505] C++: fix QL4QL warnings --- .../cpp/ir/implementation/raw/internal/TranslatedElement.qll | 2 +- .../cpp/ir/implementation/raw/internal/TranslatedGlobalVar.qll | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll index 103b5424197..38a886746ab 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll @@ -947,7 +947,7 @@ abstract class TranslatedElement extends TTranslatedElement { } /** - * Represents the IR translation of a root element, either a function or a global variable. + * The IR translation of a root element, either a function or a global variable. */ abstract class TranslatedRootElement extends TranslatedElement { TranslatedRootElement() { diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedGlobalVar.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedGlobalVar.qll index 31174d8ba5f..dde5e00361a 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedGlobalVar.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedGlobalVar.qll @@ -65,7 +65,7 @@ class TranslatedGlobalOrNamespaceVarInit extends TranslatedRootElement, result = this.getInstruction(InitializerVariableAddressTag()) or tag = InitializerVariableAddressTag() and - result = getChild(1).getFirstInstruction() + result = this.getChild(1).getFirstInstruction() or tag = ReturnTag() and result = this.getInstruction(AliasedUseTag()) From 3007c96c721e5f67b1a0775dc637d3f26e5267fa Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Mon, 1 Aug 2022 15:34:03 -0400 Subject: [PATCH 396/505] C++: fix a nit --- cpp/ql/test/library-tests/ir/ir/PrintConfig.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/test/library-tests/ir/ir/PrintConfig.qll b/cpp/ql/test/library-tests/ir/ir/PrintConfig.qll index a9167597691..bd77d831cb7 100644 --- a/cpp/ql/test/library-tests/ir/ir/PrintConfig.qll +++ b/cpp/ql/test/library-tests/ir/ir/PrintConfig.qll @@ -15,7 +15,7 @@ predicate locationIsInStandardHeaders(Location loc) { predicate shouldDumpFunction(Declaration decl) { not locationIsInStandardHeaders(decl.getLocation()) and ( - not decl instanceof Variable + decl instanceof Function or decl.(GlobalOrNamespaceVariable).hasInitializer() ) From cd356a5ac174a906f2c23bfaa34d2aabe1cffeec Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Tue, 2 Aug 2022 08:49:58 +0200 Subject: [PATCH 397/505] Java: Improve join-order. --- java/ql/lib/semmle/code/java/dispatch/WrappedInvocation.qll | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/java/ql/lib/semmle/code/java/dispatch/WrappedInvocation.qll b/java/ql/lib/semmle/code/java/dispatch/WrappedInvocation.qll index 7dd250671ad..abab4134253 100644 --- a/java/ql/lib/semmle/code/java/dispatch/WrappedInvocation.qll +++ b/java/ql/lib/semmle/code/java/dispatch/WrappedInvocation.qll @@ -19,8 +19,9 @@ private predicate runner(Method m, int n, Method runmethod) { exists(Parameter p, MethodAccess ma, int j | p = m.getParameter(n) and ma.getEnclosingCallable() = m and - runner(ma.getMethod().getSourceDeclaration(), j, _) and - ma.getArgument(j) = p.getAnAccess() + runner(pragma[only_bind_into](ma.getMethod().getSourceDeclaration()), + pragma[only_bind_into](j), _) and + ma.getArgument(pragma[only_bind_into](j)) = p.getAnAccess() ) ) } From bada5bf7c15dd0422c55b0fedaaecfb65a2f7b42 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 19 Jul 2022 13:32:51 +0100 Subject: [PATCH 398/505] Swift: Placeholder query + docs for CWE-95. --- .../Security/CWE-095/UnsafeWebViewFetch.qhelp | 32 +++++++++++++++++++ .../Security/CWE-095/UnsafeWebViewFetch.ql | 17 ++++++++++ .../CWE-095/UnsafeWebViewFetchBad.swift | 2 ++ .../CWE-095/UnsafeWebViewFetchGood.swift | 2 ++ 4 files changed, 53 insertions(+) create mode 100644 swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetch.qhelp create mode 100644 swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetch.ql create mode 100644 swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetchBad.swift create mode 100644 swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetchGood.swift diff --git a/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetch.qhelp b/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetch.qhelp new file mode 100644 index 00000000000..a58e46b5aff --- /dev/null +++ b/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetch.qhelp @@ -0,0 +1,32 @@ + + + +

    TODO

    + +
    + + +

    TODO

    + +
    + + +

    TODO

    + + + +

    TODO

    + + + +
    + + +
  • + TODO +
  • + +
    +
    \ No newline at end of file diff --git a/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetch.ql b/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetch.ql new file mode 100644 index 00000000000..2c047db4906 --- /dev/null +++ b/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetch.ql @@ -0,0 +1,17 @@ +/** + * @name Unsafe WebView fetch + * @description TODO + * @kind problem + * @problem.severity warning + * @security-severity TODO + * @precision high + * @id swift/unsafe-webview-fetch + * @tags security + * external/cwe/cwe-095 + * external/cwe/cwe-079 + * external/cwe/cwe-749 + */ + +import swift + +select "TODO" diff --git a/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetchBad.swift b/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetchBad.swift new file mode 100644 index 00000000000..6921ceac35d --- /dev/null +++ b/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetchBad.swift @@ -0,0 +1,2 @@ + +TODO diff --git a/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetchGood.swift b/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetchGood.swift new file mode 100644 index 00000000000..6921ceac35d --- /dev/null +++ b/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetchGood.swift @@ -0,0 +1,2 @@ + +TODO From 13b2b7674d350f5642380275cdff364dc2b0e63b Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Tue, 2 Aug 2022 11:28:28 +0100 Subject: [PATCH 399/505] Go: note that numeric-typed nodes can't cause path traversal --- .../lib/semmle/go/security/TaintedPathCustomizations.qll | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/go/ql/lib/semmle/go/security/TaintedPathCustomizations.qll b/go/ql/lib/semmle/go/security/TaintedPathCustomizations.qll index 2fb37ecb3fa..5bdba0ea931 100644 --- a/go/ql/lib/semmle/go/security/TaintedPathCustomizations.qll +++ b/go/ql/lib/semmle/go/security/TaintedPathCustomizations.qll @@ -70,6 +70,15 @@ module TaintedPath { PathAsSink() { this = any(FileSystemAccess fsa).getAPathArgument() } } + /** + * A numeric-typed node, considered a sanitizer for path traversal. + */ + class NumericSanitizer extends Sanitizer { + NumericSanitizer() { + this.getType() instanceof NumericType or this.getType() instanceof BoolType + } + } + /** * A call to `filepath.Rel`, considered as a sanitizer for path traversal. */ From e04a9b580564ee3a6f270e6f0384639f24651996 Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Tue, 2 Aug 2022 11:37:27 +0100 Subject: [PATCH 400/505] Add change note --- go/ql/src/change-notes/2022-08-02-path-injection-sanitizer.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 go/ql/src/change-notes/2022-08-02-path-injection-sanitizer.md diff --git a/go/ql/src/change-notes/2022-08-02-path-injection-sanitizer.md b/go/ql/src/change-notes/2022-08-02-path-injection-sanitizer.md new file mode 100644 index 00000000000..1c45e8d14e5 --- /dev/null +++ b/go/ql/src/change-notes/2022-08-02-path-injection-sanitizer.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* The query `go/path-injection` no longer considers user-controlled numeric or boolean-typed data as potentially dangerous. From 80bba605e3a3f3ceb4c878a2fbe2c5e64a54f45e Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Tue, 2 Aug 2022 12:49:21 +0200 Subject: [PATCH 401/505] Java: Fix join-order in SameNameAsSuper. --- .../Naming Conventions/SameNameAsSuper.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/src/Violations of Best Practice/Naming Conventions/SameNameAsSuper.ql b/java/ql/src/Violations of Best Practice/Naming Conventions/SameNameAsSuper.ql index 79f5f2cf473..a9f99658f94 100644 --- a/java/ql/src/Violations of Best Practice/Naming Conventions/SameNameAsSuper.ql +++ b/java/ql/src/Violations of Best Practice/Naming Conventions/SameNameAsSuper.ql @@ -16,5 +16,5 @@ from RefType sub, RefType sup where sub.fromSource() and sup = sub.getASupertype() and - sub.getName() = sup.getName() + pragma[only_bind_out](sub.getName()) = pragma[only_bind_out](sup.getName()) select sub, sub.getName() + " has the same name as its supertype $@.", sup, sup.getQualifiedName() From aabdf8430075c4d1bc930799f0c68f4d3792c5ca Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Tue, 2 Aug 2022 14:29:03 +0200 Subject: [PATCH 402/505] Java: Improve join-order for `not haveIntersection`. --- java/ql/lib/semmle/code/java/Type.qll | 23 +++++++++++++++++-- .../Collections/ContainsTypeMismatch.ql | 4 ++-- .../Collections/RemoveTypeMismatch.ql | 4 ++-- .../Comparison/IncomparableEquals.ql | 2 +- 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/java/ql/lib/semmle/code/java/Type.qll b/java/ql/lib/semmle/code/java/Type.qll index 2e83c31f26a..256df5d66f3 100755 --- a/java/ql/lib/semmle/code/java/Type.qll +++ b/java/ql/lib/semmle/code/java/Type.qll @@ -1194,8 +1194,8 @@ private Type erase(Type t) { } /** - * Is there a common (reflexive, transitive) subtype of the erasures of - * types `t1` and `t2`? + * Holds if there is a common (reflexive, transitive) subtype of the erasures of + * types `t1` and `t2`. * * If there is no such common subtype, then the two types are disjoint. * However, the converse is not true; for example, the parameterized types @@ -1212,6 +1212,25 @@ predicate haveIntersection(RefType t1, RefType t2) { ) } +/** + * Holds if there is no common (reflexive, transitive) subtype of the erasures + * of types `t1` and `t2`. + * + * If there is no such common subtype, then the two types are disjoint. + * However, the converse is not true; for example, the parameterized types + * `List` and `Collection` are disjoint, + * but their erasures (`List` and `Collection`, respectively) + * do have common subtypes (such as `List` itself). + * + * For the definition of the notion of *erasure* see JLS v8, section 4.6 (Type Erasure). + */ +bindingset[t1, t2] +predicate notHaveIntersection(RefType t1, RefType t2) { + exists(RefType e1, RefType e2 | e1 = erase(t1) and e2 = erase(t2) | + not erasedHaveIntersection(e1, e2) + ) +} + /** * Holds if there is a common (reflexive, transitive) subtype of the erased * types `t1` and `t2`. diff --git a/java/ql/src/Likely Bugs/Collections/ContainsTypeMismatch.ql b/java/ql/src/Likely Bugs/Collections/ContainsTypeMismatch.ql index b34830c3537..52d790e0e71 100644 --- a/java/ql/src/Likely Bugs/Collections/ContainsTypeMismatch.ql +++ b/java/ql/src/Likely Bugs/Collections/ContainsTypeMismatch.ql @@ -118,7 +118,7 @@ class MismatchedContainerAccess extends MethodAccess { containerAccess(package, type, p, this.getCallee().getSignature(), i) | t = this.getCallee().getDeclaringType() and - t.getAnAncestor().getSourceDeclaration() = g and + t.getASourceSupertype*().getSourceDeclaration() = g and g.hasQualifiedName(package, type) and indirectlyInstantiates(t, g, p, result) ) @@ -139,7 +139,7 @@ from MismatchedContainerAccess ma, RefType typearg, RefType argtype, int idx where typearg = ma.getReceiverElementType(idx).getSourceDeclaration() and argtype = ma.getArgumentType(idx) and - not haveIntersection(typearg, argtype) + notHaveIntersection(typearg, argtype) select ma.getArgument(idx), "Actual argument type '" + argtype.getName() + "'" + " is incompatible with expected argument type '" + typearg.getName() + "'." diff --git a/java/ql/src/Likely Bugs/Collections/RemoveTypeMismatch.ql b/java/ql/src/Likely Bugs/Collections/RemoveTypeMismatch.ql index 8fa467c2d8a..076ccc12240 100644 --- a/java/ql/src/Likely Bugs/Collections/RemoveTypeMismatch.ql +++ b/java/ql/src/Likely Bugs/Collections/RemoveTypeMismatch.ql @@ -88,7 +88,7 @@ class MismatchedContainerModification extends MethodAccess { containerModification(package, type, p, this.getCallee().getSignature(), i) | t = this.getCallee().getDeclaringType() and - t.getAnAncestor().getSourceDeclaration() = g and + t.getASourceSupertype*().getSourceDeclaration() = g and g.hasQualifiedName(package, type) and indirectlyInstantiates(t, g, p, result) ) @@ -109,7 +109,7 @@ from MismatchedContainerModification ma, RefType elementtype, RefType argtype, i where elementtype = ma.getReceiverElementType(idx).getSourceDeclaration() and argtype = ma.getArgumentType(idx) and - not haveIntersection(elementtype, argtype) + notHaveIntersection(elementtype, argtype) select ma.getArgument(idx), "Actual argument type '" + argtype.getName() + "'" + " is incompatible with expected argument type '" + elementtype.getName() + "'." diff --git a/java/ql/src/Likely Bugs/Comparison/IncomparableEquals.ql b/java/ql/src/Likely Bugs/Comparison/IncomparableEquals.ql index c083c80c21d..d98fc77af38 100644 --- a/java/ql/src/Likely Bugs/Comparison/IncomparableEquals.ql +++ b/java/ql/src/Likely Bugs/Comparison/IncomparableEquals.ql @@ -57,7 +57,7 @@ where else recvtp = ma.getMethod().getDeclaringType() ) and argtp = ma.getArgumentType() and - not haveIntersection(recvtp, argtp) + notHaveIntersection(recvtp, argtp) select ma, "Call to equals() comparing incomparable types " + recvtp.getName() + " and " + argtp.getName() + "." From 5181cc1295405e7fdcacc5df3cfd8b645d17c45c Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Tue, 2 Aug 2022 13:43:01 +0100 Subject: [PATCH 403/505] C++: Add a 'allowInterproceduralFlow' predicate to the 'MustFlow' library to and use it instead of checking the enclosing callables after computing the dataflow graph. --- .../semmle/code/cpp/ir/dataflow/MustFlow.qll | 20 ++++++++++++++++++- .../ReturnStackAllocatedMemory.ql | 17 ++++++++++++---- .../ReturnStackAllocatedMemory.expected | 13 ------------ 3 files changed, 32 insertions(+), 18 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/MustFlow.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/MustFlow.qll index 1f3ea2a4d3d..08ee06acdda 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/MustFlow.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/MustFlow.qll @@ -38,6 +38,9 @@ abstract class MustFlowConfiguration extends string { */ predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { none() } + /** Holds if this configuration allows flow from arguments to parameters. */ + predicate allowInterproceduralFlow() { any() } + /** * Holds if data must flow from `source` to `sink` for this configuration. * @@ -204,10 +207,25 @@ private module Cached { } } +/** + * Gets the enclosing callable of `n`. Unlike `n.getEnclosingCallable()`, this + * predicate ensures that joins go from `n` to the result instead of the other + * way around. + */ +pragma[inline] +private Declaration getEnclosingCallable(DataFlow::Node n) { + pragma[only_bind_into](result) = pragma[only_bind_out](n).getEnclosingCallable() +} + /** Holds if `nodeFrom` flows to `nodeTo`. */ private predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, MustFlowConfiguration config) { exists(config) and - Cached::step(nodeFrom, nodeTo) + Cached::step(pragma[only_bind_into](nodeFrom), pragma[only_bind_into](nodeTo)) and + ( + config.allowInterproceduralFlow() + or + getEnclosingCallable(nodeFrom) = getEnclosingCallable(nodeTo) + ) or config.isAdditionalFlowStep(nodeFrom, nodeTo) } diff --git a/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql b/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql index 7eab1bd03c8..ed1d4084993 100644 --- a/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql +++ b/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql @@ -52,6 +52,18 @@ class ReturnStackAllocatedMemoryConfig extends MustFlowConfiguration { ) } + // We disable flow into callables in this query as we'd otherwise get a result on this piece of code: + // ```cpp + // int* id(int* px) { + // return px; // this returns the local variable `x`, but it's fine as the local variable isn't declared in this scope. + // } + // void f() { + // int x; + // int* px = id(&x); + // } + // ``` + override predicate allowInterproceduralFlow() { none() } + /** * This configuration intentionally conflates addresses of fields and their object, and pointer offsets * with their base pointer as this allows us to detect cases where an object's address flows to a @@ -77,9 +89,6 @@ from ReturnStackAllocatedMemoryConfig conf where conf.hasFlowPath(pragma[only_bind_into](source), pragma[only_bind_into](sink)) and - source.getNode().asInstruction() = var and - // Only raise an alert if we're returning from the _same_ callable as the on that - // declared the stack variable. - var.getEnclosingFunction() = sink.getNode().getEnclosingCallable() + source.getNode().asInstruction() = var select sink.getNode(), source, sink, "May return stack-allocated memory from $@.", var.getAst(), var.getAst().toString() diff --git a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/ReturnStackAllocatedMemory.expected b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/ReturnStackAllocatedMemory.expected index 6b8a59793a3..8f9d91fc1ad 100644 --- a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/ReturnStackAllocatedMemory.expected +++ b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/ReturnStackAllocatedMemory.expected @@ -100,12 +100,6 @@ edges | test.cpp:190:10:190:13 | Unary | test.cpp:190:10:190:13 | (reference dereference) | | test.cpp:190:10:190:13 | Unary | test.cpp:190:10:190:13 | (reference to) | | test.cpp:190:10:190:13 | pRef | test.cpp:190:10:190:13 | Unary | -| test.cpp:225:14:225:15 | px | test.cpp:226:10:226:11 | Load | -| test.cpp:226:10:226:11 | Load | test.cpp:226:10:226:11 | px | -| test.cpp:226:10:226:11 | px | test.cpp:226:10:226:11 | StoreValue | -| test.cpp:231:16:231:17 | & ... | test.cpp:225:14:225:15 | px | -| test.cpp:231:17:231:17 | Unary | test.cpp:231:16:231:17 | & ... | -| test.cpp:231:17:231:17 | x | test.cpp:231:17:231:17 | Unary | nodes | test.cpp:17:9:17:11 | & ... | semmle.label | & ... | | test.cpp:17:9:17:11 | StoreValue | semmle.label | StoreValue | @@ -221,13 +215,6 @@ nodes | test.cpp:190:10:190:13 | Unary | semmle.label | Unary | | test.cpp:190:10:190:13 | Unary | semmle.label | Unary | | test.cpp:190:10:190:13 | pRef | semmle.label | pRef | -| test.cpp:225:14:225:15 | px | semmle.label | px | -| test.cpp:226:10:226:11 | Load | semmle.label | Load | -| test.cpp:226:10:226:11 | StoreValue | semmle.label | StoreValue | -| test.cpp:226:10:226:11 | px | semmle.label | px | -| test.cpp:231:16:231:17 | & ... | semmle.label | & ... | -| test.cpp:231:17:231:17 | Unary | semmle.label | Unary | -| test.cpp:231:17:231:17 | x | semmle.label | x | #select | test.cpp:17:9:17:11 | StoreValue | test.cpp:17:10:17:11 | mc | test.cpp:17:9:17:11 | StoreValue | May return stack-allocated memory from $@. | test.cpp:17:10:17:11 | mc | mc | | test.cpp:25:9:25:11 | StoreValue | test.cpp:23:18:23:19 | mc | test.cpp:25:9:25:11 | StoreValue | May return stack-allocated memory from $@. | test.cpp:23:18:23:19 | mc | mc | From f385041ab344d74491db112b0873faead4db71b7 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Tue, 2 Aug 2022 14:07:22 +0100 Subject: [PATCH 404/505] C++: Add change note. --- .../lib/change-notes/2022-08-02-must-flow-local-only-flow.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 cpp/ql/lib/change-notes/2022-08-02-must-flow-local-only-flow.md diff --git a/cpp/ql/lib/change-notes/2022-08-02-must-flow-local-only-flow.md b/cpp/ql/lib/change-notes/2022-08-02-must-flow-local-only-flow.md new file mode 100644 index 00000000000..820822a5396 --- /dev/null +++ b/cpp/ql/lib/change-notes/2022-08-02-must-flow-local-only-flow.md @@ -0,0 +1,4 @@ +--- +category: feature +--- +* A new class predicate `MustFlowConfiguration::allowInterproceduralFlow` has been added to the `semmle.code.cpp.ir.dataflow.MustFlow` library. The new predicate can be overridden to disable interprocedural flow. From 64704057cbb8424fec2190fb011712fa323fd36b Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Tue, 2 Aug 2022 16:33:21 +0200 Subject: [PATCH 405/505] CI: fix path triggers --- .github/workflows/check-qldoc.yml | 2 +- .github/workflows/csv-coverage-metrics.yml | 2 +- .github/workflows/csv-coverage-pr-artifacts.yml | 2 +- .github/workflows/go-tests.yml | 2 +- .github/workflows/js-ml-tests.yml | 4 ++-- .github/workflows/mad_regenerate-models.yml | 2 +- .github/workflows/query-list.yml | 2 +- .github/workflows/ruby-build.yml | 4 ++-- .github/workflows/ruby-qltest.yml | 4 ++-- .github/workflows/swift-codegen.yml | 2 +- .github/workflows/swift-integration-tests.yml | 2 +- .github/workflows/swift-qltest.yml | 2 +- .github/workflows/validate-change-notes.yml | 4 ++-- 13 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/workflows/check-qldoc.yml b/.github/workflows/check-qldoc.yml index be986d5ecf6..cc7523162aa 100644 --- a/.github/workflows/check-qldoc.yml +++ b/.github/workflows/check-qldoc.yml @@ -5,7 +5,7 @@ on: paths: - "*/ql/lib/**" - .github/workflows/check-qldoc.yml - - .github/actions/fetch-codeql + - .github/actions/fetch-codeql/action.yml branches: - main - "rc/*" diff --git a/.github/workflows/csv-coverage-metrics.yml b/.github/workflows/csv-coverage-metrics.yml index e263572398e..7555533ab98 100644 --- a/.github/workflows/csv-coverage-metrics.yml +++ b/.github/workflows/csv-coverage-metrics.yml @@ -12,7 +12,7 @@ on: - main paths: - ".github/workflows/csv-coverage-metrics.yml" - - ".github/actions/fetch-codeql" + - ".github/actions/fetch-codeql/action.yml" jobs: publish-java: diff --git a/.github/workflows/csv-coverage-pr-artifacts.yml b/.github/workflows/csv-coverage-pr-artifacts.yml index b63d85534b4..51e4dc73b39 100644 --- a/.github/workflows/csv-coverage-pr-artifacts.yml +++ b/.github/workflows/csv-coverage-pr-artifacts.yml @@ -5,7 +5,7 @@ on: paths: - ".github/workflows/csv-coverage-pr-comment.yml" - ".github/workflows/csv-coverage-pr-artifacts.yml" - - ".github/actions/fetch-codeql" + - ".github/actions/fetch-codeql/action.yml" - "*/ql/src/**/*.ql" - "*/ql/src/**/*.qll" - "*/ql/lib/**/*.ql" diff --git a/.github/workflows/go-tests.yml b/.github/workflows/go-tests.yml index 6001a18aad1..c1d3c28b809 100644 --- a/.github/workflows/go-tests.yml +++ b/.github/workflows/go-tests.yml @@ -4,7 +4,7 @@ on: paths: - "go/**" - .github/workflows/go-tests.yml - - .github/actions/fetch-codeql + - .github/actions/fetch-codeql/action.yml - codeql-workspace.yml jobs: test-linux: diff --git a/.github/workflows/js-ml-tests.yml b/.github/workflows/js-ml-tests.yml index 0b23f91ed48..c932432530b 100644 --- a/.github/workflows/js-ml-tests.yml +++ b/.github/workflows/js-ml-tests.yml @@ -5,7 +5,7 @@ on: paths: - "javascript/ql/experimental/adaptivethreatmodeling/**" - .github/workflows/js-ml-tests.yml - - .github/actions/fetch-codeql + - .github/actions/fetch-codeql/action.yml - codeql-workspace.yml branches: - main @@ -14,7 +14,7 @@ on: paths: - "javascript/ql/experimental/adaptivethreatmodeling/**" - .github/workflows/js-ml-tests.yml - - .github/actions/fetch-codeql + - .github/actions/fetch-codeql/action.yml - codeql-workspace.yml workflow_dispatch: diff --git a/.github/workflows/mad_regenerate-models.yml b/.github/workflows/mad_regenerate-models.yml index 9f16c223ec6..0abc8936911 100644 --- a/.github/workflows/mad_regenerate-models.yml +++ b/.github/workflows/mad_regenerate-models.yml @@ -9,7 +9,7 @@ on: - main paths: - ".github/workflows/mad_regenerate-models.yml" - - ".github/actions/fetch-codeql" + - ".github/actions/fetch-codeql/action.yml" jobs: regenerate-models: diff --git a/.github/workflows/query-list.yml b/.github/workflows/query-list.yml index 0cf1cf30422..efb295dfcf8 100644 --- a/.github/workflows/query-list.yml +++ b/.github/workflows/query-list.yml @@ -10,7 +10,7 @@ on: pull_request: paths: - '.github/workflows/query-list.yml' - - '.github/actions/fetch-codeql' + - '.github/actions/fetch-codeql/action.yml' - 'misc/scripts/generate-code-scanning-query-list.py' jobs: diff --git a/.github/workflows/ruby-build.yml b/.github/workflows/ruby-build.yml index 2f7464e47b3..6ad627aab48 100644 --- a/.github/workflows/ruby-build.yml +++ b/.github/workflows/ruby-build.yml @@ -5,7 +5,7 @@ on: paths: - "ruby/**" - .github/workflows/ruby-build.yml - - .github/actions/fetch-codeql + - .github/actions/fetch-codeql/action.yml - codeql-workspace.yml branches: - main @@ -14,7 +14,7 @@ on: paths: - "ruby/**" - .github/workflows/ruby-build.yml - - .github/actions/fetch-codeql + - .github/actions/fetch-codeql/action.yml - codeql-workspace.yml branches: - main diff --git a/.github/workflows/ruby-qltest.yml b/.github/workflows/ruby-qltest.yml index e5eb7e05ecd..97235b722ba 100644 --- a/.github/workflows/ruby-qltest.yml +++ b/.github/workflows/ruby-qltest.yml @@ -5,7 +5,7 @@ on: paths: - "ruby/**" - .github/workflows/ruby-qltest.yml - - .github/actions/fetch-codeql + - .github/actions/fetch-codeql/action.yml - codeql-workspace.yml branches: - main @@ -14,7 +14,7 @@ on: paths: - "ruby/**" - .github/workflows/ruby-qltest.yml - - .github/actions/fetch-codeql + - .github/actions/fetch-codeql/action.yml - codeql-workspace.yml branches: - main diff --git a/.github/workflows/swift-codegen.yml b/.github/workflows/swift-codegen.yml index 665ee55a247..5700045430d 100644 --- a/.github/workflows/swift-codegen.yml +++ b/.github/workflows/swift-codegen.yml @@ -5,7 +5,7 @@ on: paths: - "swift/**" - .github/workflows/swift-codegen.yml - - .github/actions/fetch-codeql + - .github/actions/fetch-codeql/action.yml branches: - main diff --git a/.github/workflows/swift-integration-tests.yml b/.github/workflows/swift-integration-tests.yml index cc365809c73..4d4248b64e3 100644 --- a/.github/workflows/swift-integration-tests.yml +++ b/.github/workflows/swift-integration-tests.yml @@ -5,7 +5,7 @@ on: paths: - "swift/**" - .github/workflows/swift-integration-tests.yml - - .github/actions/fetch-codeql + - .github/actions/fetch-codeql/action.yml - codeql-workspace.yml branches: - main diff --git a/.github/workflows/swift-qltest.yml b/.github/workflows/swift-qltest.yml index 76a21b0bd8a..3cbcf629c98 100644 --- a/.github/workflows/swift-qltest.yml +++ b/.github/workflows/swift-qltest.yml @@ -5,7 +5,7 @@ on: paths: - "swift/**" - .github/workflows/swift-qltest.yml - - .github/actions/fetch-codeql + - .github/actions/fetch-codeql/action.yml - codeql-workspace.yml branches: - main diff --git a/.github/workflows/validate-change-notes.yml b/.github/workflows/validate-change-notes.yml index b06167ea905..44e0dc6df29 100644 --- a/.github/workflows/validate-change-notes.yml +++ b/.github/workflows/validate-change-notes.yml @@ -5,7 +5,7 @@ on: paths: - "*/ql/*/change-notes/**/*" - ".github/workflows/validate-change-notes.yml" - - ".github/actions/fetch-codeql" + - ".github/actions/fetch-codeql/action.yml" branches: - main - "rc/*" @@ -13,7 +13,7 @@ on: paths: - "*/ql/*/change-notes/**/*" - ".github/workflows/validate-change-notes.yml" - - ".github/actions/fetch-codeql" + - ".github/actions/fetch-codeql/action.yml" jobs: check-change-note: From b21fa0e2b0c25c3cd36877b774f522eec22d7eb2 Mon Sep 17 00:00:00 2001 From: Keith Hoodlet <22803099+securingdev@users.noreply.github.com> Date: Tue, 2 Aug 2022 10:49:45 -0400 Subject: [PATCH 406/505] Update Other section with example exit code Add troubleshooting steps to remediate issues with the kernel killing a process. --- docs/codeql/codeql-cli/exit-codes.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/codeql/codeql-cli/exit-codes.rst b/docs/codeql/codeql-cli/exit-codes.rst index 5d9a0d434b6..04d4d93dc07 100644 --- a/docs/codeql/codeql-cli/exit-codes.rst +++ b/docs/codeql/codeql-cli/exit-codes.rst @@ -71,4 +71,4 @@ Other ----- In the case of really severe problems within the JVM that runs ``codeql``, it might return a nonzero exit code of its own choosing. -This should only happen if something is severely wrong with the CodeQL installation. \ No newline at end of file +This should only happen if something is severely wrong with the CodeQL installation, or a memory issue with the host system running the CodeQL process. For example, Unix systems may return `Exit Code 137` to indicate that the kernel has killed a process that CodeQL has started. One way to troubleshoot this issue is to modify your `--ram=` flag for the `codeql database analyze` step and re-run your workflow. From 759fd6cc0b0f8ff9396c36ecb5dabf3b5c597408 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Mon, 1 Aug 2022 12:56:57 +0200 Subject: [PATCH 407/505] Use 'gh codeql' with the nightly release for CI jobs --- .github/actions/fetch-codeql/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/fetch-codeql/action.yml b/.github/actions/fetch-codeql/action.yml index d1f48f40047..02098892663 100644 --- a/.github/actions/fetch-codeql/action.yml +++ b/.github/actions/fetch-codeql/action.yml @@ -7,7 +7,7 @@ runs: shell: bash run: | gh extension install github/gh-codeql - gh codeql set-channel release + gh codeql set-channel nightly gh codeql version gh codeql version --format=json | jq -r .unpackedLocation >> "${GITHUB_PATH}" env: From c95f17fdf2975b94caf714d1a4683c73ab3ee9f3 Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Tue, 2 Aug 2022 21:28:00 +0100 Subject: [PATCH 408/505] Make java/path-injection recognise create-file MaD sinks --- java/ql/src/Security/CWE/CWE-022/TaintedPath.ql | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/java/ql/src/Security/CWE/CWE-022/TaintedPath.ql b/java/ql/src/Security/CWE/CWE-022/TaintedPath.ql index 306b835b98b..9e1a13b81ea 100644 --- a/java/ql/src/Security/CWE/CWE-022/TaintedPath.ql +++ b/java/ql/src/Security/CWE/CWE-022/TaintedPath.ql @@ -34,7 +34,12 @@ class TaintedPathConfig extends TaintTracking::Configuration { override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } override predicate isSink(DataFlow::Node sink) { - exists(Expr e | e = sink.asExpr() | e = any(PathCreation p).getAnInput() and not guarded(e)) + ( + sink.asExpr() = any(PathCreation p).getAnInput() + or + sinkNode(sink, "create-file") + ) and + not guarded(sink.asExpr()) } override predicate isSanitizer(DataFlow::Node node) { From 81f3bcd80249f76826c46eca5c2e4e99bbe8c409 Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Tue, 2 Aug 2022 21:30:06 +0100 Subject: [PATCH 409/505] Don't require a PathCreation for every tainted-path sink --- java/ql/src/Security/CWE/CWE-022/TaintedPath.ql | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/java/ql/src/Security/CWE/CWE-022/TaintedPath.ql b/java/ql/src/Security/CWE/CWE-022/TaintedPath.ql index 9e1a13b81ea..8743673eea7 100644 --- a/java/ql/src/Security/CWE/CWE-022/TaintedPath.ql +++ b/java/ql/src/Security/CWE/CWE-022/TaintedPath.ql @@ -49,9 +49,7 @@ class TaintedPathConfig extends TaintTracking::Configuration { } } -from DataFlow::PathNode source, DataFlow::PathNode sink, PathCreation p, TaintedPathConfig conf -where - sink.getNode().asExpr() = p.getAnInput() and - conf.hasFlowPath(source, sink) -select p, source, sink, "$@ flows to here and is used in a path.", source.getNode(), +from DataFlow::PathNode source, DataFlow::PathNode sink, TaintedPathConfig conf +where conf.hasFlowPath(source, sink) +select sink, source, sink, "$@ flows to here and is used in a path.", source.getNode(), "User-provided value" From d8592a2b05592905227aeedfe9973c1b3f398d35 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Wed, 3 Aug 2022 09:02:38 +0200 Subject: [PATCH 410/505] Ruby: PrintAST: more stable order for synthesized nodes --- ruby/ql/lib/codeql/ruby/printAst.qll | 12 +++- .../library-tests/ast/AstDesugar.expected | 56 +++++++++---------- 2 files changed, 39 insertions(+), 29 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/printAst.qll b/ruby/ql/lib/codeql/ruby/printAst.qll index 3056e9aa49f..d66e7fe0535 100644 --- a/ruby/ql/lib/codeql/ruby/printAst.qll +++ b/ruby/ql/lib/codeql/ruby/printAst.qll @@ -9,6 +9,7 @@ private import AST private import codeql.ruby.Regexp as RE private import codeql.ruby.ast.internal.Synthesis +private import ast.internal.AST /** * The query can extend this class to control which nodes are printed. @@ -112,13 +113,22 @@ class PrintRegularAstNode extends PrintAstNode, TPrintRegularAstNode { ) } + private int getSynthAstNodeIndex() { + not astNode.isSynthesized() and result = -10 + or + astNode = getSynthChild(astNode.getParent(), result) + } + override int getOrder() { this = rank[result](PrintRegularAstNode p, Location l, File f | l = p.getLocation() and f = l.getFile() | - p order by f.getBaseName(), f.getAbsolutePath(), l.getStartLine(), l.getStartColumn() + p + order by + f.getBaseName(), f.getAbsolutePath(), l.getStartLine(), l.getStartColumn(), + l.getEndLine(), l.getEndColumn(), p.getSynthAstNodeIndex() ) } diff --git a/ruby/ql/test/library-tests/ast/AstDesugar.expected b/ruby/ql/test/library-tests/ast/AstDesugar.expected index 956893e944f..8be5246ab88 100644 --- a/ruby/ql/test/library-tests/ast/AstDesugar.expected +++ b/ruby/ql/test/library-tests/ast/AstDesugar.expected @@ -86,10 +86,10 @@ calls/calls.rb: # 316| getStmt: [SetterMethodCall] call to foo= # 316| getReceiver: [SelfVariableAccess] self # 316| getArgument: [AssignExpr] ... = ... -# 316| getAnOperand/getRightOperand: [MethodCall] call to [] -# 316| getArgument: [IntegerLiteral] 0 -# 316| getReceiver: [LocalVariableAccess] __synth__0 # 316| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1 +# 316| getAnOperand/getRightOperand: [MethodCall] call to [] +# 316| getReceiver: [LocalVariableAccess] __synth__0 +# 316| getArgument: [IntegerLiteral] 0 # 316| getStmt: [LocalVariableAccess] __synth__0__1 # 316| getStmt: [AssignExpr] ... = ... # 316| getAnOperand/getLeftOperand: [MethodCall] call to bar @@ -97,12 +97,12 @@ calls/calls.rb: # 316| getStmt: [SetterMethodCall] call to bar= # 316| getReceiver: [SelfVariableAccess] self # 316| getArgument: [AssignExpr] ... = ... +# 316| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1 # 316| getAnOperand/getRightOperand: [MethodCall] call to [] +# 316| getReceiver: [LocalVariableAccess] __synth__0 # 316| getArgument: [RangeLiteral] _ .. _ # 316| getBegin: [IntegerLiteral] 1 # 316| getEnd: [IntegerLiteral] -2 -# 316| getReceiver: [LocalVariableAccess] __synth__0 -# 316| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1 # 316| getStmt: [LocalVariableAccess] __synth__0__1 # 316| getStmt: [AssignExpr] ... = ... # 316| getAnOperand/getLeftOperand: [ElementReference] ...[...] @@ -111,13 +111,14 @@ calls/calls.rb: # 316| getReceiver: [MethodCall] call to foo # 316| getReceiver: [SelfVariableAccess] self # 316| getArgument: [AssignExpr] ... = ... -# 316| getAnOperand/getRightOperand: [MethodCall] call to [] -# 316| getArgument: [IntegerLiteral] -1 -# 316| getReceiver: [LocalVariableAccess] __synth__0 # 316| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1 +# 316| getAnOperand/getRightOperand: [MethodCall] call to [] +# 316| getReceiver: [LocalVariableAccess] __synth__0 +# 316| getArgument: [IntegerLiteral] -1 # 316| getArgument: [IntegerLiteral] 4 # 316| getStmt: [LocalVariableAccess] __synth__0__1 # 316| getStmt: [AssignExpr] ... = ... +# 316| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0 # 316| getAnOperand/getRightOperand: [SplatExpr] * ... # 316| getAnOperand/getOperand/getReceiver: [ArrayLiteral] [...] # 316| getDesugared: [MethodCall] call to [] @@ -126,14 +127,13 @@ calls/calls.rb: # 316| getArgument: [IntegerLiteral] 2 # 316| getArgument: [IntegerLiteral] 3 # 316| getArgument: [IntegerLiteral] 4 -# 316| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0 # 317| [AssignExpr] ... = ... # 317| getDesugared: [StmtSequence] ... # 317| getStmt: [AssignExpr] ... = ... # 317| getAnOperand/getLeftOperand: [LocalVariableAccess] a # 317| getAnOperand/getRightOperand: [MethodCall] call to [] -# 317| getArgument: [IntegerLiteral] 0 # 317| getReceiver: [LocalVariableAccess] __synth__0 +# 317| getArgument: [IntegerLiteral] 0 # 317| getStmt: [AssignExpr] ... = ... # 317| getAnOperand/getLeftOperand: [ElementReference] ...[...] # 317| getDesugared: [StmtSequence] ... @@ -141,15 +141,16 @@ calls/calls.rb: # 317| getReceiver: [MethodCall] call to foo # 317| getReceiver: [SelfVariableAccess] self # 317| getArgument: [AssignExpr] ... = ... +# 317| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1 # 317| getAnOperand/getRightOperand: [MethodCall] call to [] +# 317| getReceiver: [LocalVariableAccess] __synth__0 # 317| getArgument: [RangeLiteral] _ .. _ # 317| getBegin: [IntegerLiteral] 1 # 317| getEnd: [IntegerLiteral] -1 -# 317| getReceiver: [LocalVariableAccess] __synth__0 -# 317| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1 # 317| getArgument: [IntegerLiteral] 5 # 317| getStmt: [LocalVariableAccess] __synth__0__1 # 317| getStmt: [AssignExpr] ... = ... +# 317| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0 # 317| getAnOperand/getRightOperand: [SplatExpr] * ... # 317| getAnOperand/getOperand/getReceiver: [ArrayLiteral] [...] # 317| getDesugared: [MethodCall] call to [] @@ -157,7 +158,6 @@ calls/calls.rb: # 317| getArgument: [IntegerLiteral] 1 # 317| getArgument: [IntegerLiteral] 2 # 317| getArgument: [IntegerLiteral] 3 -# 317| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0 # 318| [AssignAddExpr] ... += ... # 318| getDesugared: [StmtSequence] ... # 318| getStmt: [AssignExpr] ... = ... @@ -167,11 +167,11 @@ calls/calls.rb: # 318| getReceiver: [LocalVariableAccess] __synth__0 # 318| getArgument: [LocalVariableAccess] __synth__1 # 318| getStmt: [AssignExpr] ... = ... +# 318| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__1 # 318| getAnOperand/getRightOperand: [AddExpr] ... + ... # 318| getAnOperand/getLeftOperand/getReceiver: [MethodCall] call to count # 318| getReceiver: [LocalVariableAccess] __synth__0 # 318| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 1 -# 318| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__1 # 318| getStmt: [LocalVariableAccess] __synth__1 # 319| [AssignAddExpr] ... += ... # 319| getDesugared: [StmtSequence] ... @@ -187,12 +187,12 @@ calls/calls.rb: # 319| getAnOperand/getRightOperand: [IntegerLiteral] 0 # 319| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__1 # 319| getStmt: [AssignExpr] ... = ... +# 319| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__2 # 319| getAnOperand/getRightOperand: [AddExpr] ... + ... # 319| getAnOperand/getLeftOperand/getReceiver: [MethodCall] call to [] # 319| getReceiver: [LocalVariableAccess] __synth__0 # 319| getArgument: [LocalVariableAccess] __synth__1 # 319| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 1 -# 319| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__2 # 319| getStmt: [LocalVariableAccess] __synth__2 # 320| [AssignMulExpr] ... *= ... # 320| getDesugared: [StmtSequence] ... @@ -223,6 +223,7 @@ calls/calls.rb: # 320| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 1 # 320| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__3 # 320| getStmt: [AssignExpr] ... = ... +# 320| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__4 # 320| getAnOperand/getRightOperand: [MulExpr] ... * ... # 320| getAnOperand/getLeftOperand/getReceiver: [MethodCall] call to [] # 320| getReceiver: [LocalVariableAccess] __synth__0 @@ -230,7 +231,6 @@ calls/calls.rb: # 320| getArgument: [LocalVariableAccess] __synth__2 # 320| getArgument: [LocalVariableAccess] __synth__3 # 320| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 2 -# 320| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__4 # 320| getStmt: [LocalVariableAccess] __synth__4 # 340| [ForExpr] for ... in ... # 340| getDesugared: [MethodCall] call to each @@ -240,24 +240,24 @@ calls/calls.rb: # 340| getStmt: [AssignExpr] ... = ... # 340| getDesugared: [StmtSequence] ... # 340| getStmt: [AssignExpr] ... = ... +# 340| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1 # 340| getAnOperand/getRightOperand: [SplatExpr] * ... # 340| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] __synth__0__1 -# 340| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1 # 340| getStmt: [AssignExpr] ... = ... # 340| getAnOperand/getLeftOperand: [LocalVariableAccess] x # 340| getAnOperand/getRightOperand: [MethodCall] call to [] -# 340| getArgument: [IntegerLiteral] 0 # 340| getReceiver: [LocalVariableAccess] __synth__0__1 +# 340| getArgument: [IntegerLiteral] 0 # 340| getStmt: [AssignExpr] ... = ... # 340| getAnOperand/getLeftOperand: [LocalVariableAccess] y # 340| getAnOperand/getRightOperand: [MethodCall] call to [] -# 340| getArgument: [IntegerLiteral] 1 # 340| getReceiver: [LocalVariableAccess] __synth__0__1 +# 340| getArgument: [IntegerLiteral] 1 # 340| getStmt: [AssignExpr] ... = ... # 340| getAnOperand/getLeftOperand: [LocalVariableAccess] z # 340| getAnOperand/getRightOperand: [MethodCall] call to [] -# 340| getArgument: [IntegerLiteral] 2 # 340| getReceiver: [LocalVariableAccess] __synth__0__1 +# 340| getArgument: [IntegerLiteral] 2 # 340| getAnOperand/getLeftOperand: [DestructuredLhsExpr] (..., ...) # 341| getStmt: [MethodCall] call to foo # 341| getReceiver: [SelfVariableAccess] self @@ -286,9 +286,9 @@ calls/calls.rb: # 362| getReceiver: [SelfVariableAccess] self # 362| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1 # 362| getStmt: [IfExpr] if ... +# 362| getBranch/getThen: [NilLiteral] nil # 362| getBranch/getElse: [MethodCall] call to empty? # 362| getReceiver: [LocalVariableAccess] __synth__0__1 -# 362| getBranch/getThen: [NilLiteral] nil # 362| getCondition: [MethodCall] call to == # 362| getArgument: [LocalVariableAccess] __synth__0__1 # 362| getReceiver: [NilLiteral] nil @@ -299,6 +299,7 @@ calls/calls.rb: # 364| getReceiver: [SelfVariableAccess] self # 364| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1 # 364| getStmt: [IfExpr] if ... +# 364| getBranch/getThen: [NilLiteral] nil # 364| getBranch/getElse: [MethodCall] call to bar # 364| getReceiver: [LocalVariableAccess] __synth__0__1 # 364| getArgument: [IntegerLiteral] 1 @@ -307,7 +308,6 @@ calls/calls.rb: # 364| getParameter: [SimpleParameter] x # 364| getDefiningAccess: [LocalVariableAccess] x # 364| getStmt: [LocalVariableAccess] x -# 364| getBranch/getThen: [NilLiteral] nil # 364| getCondition: [MethodCall] call to == # 364| getArgument: [LocalVariableAccess] __synth__0__1 # 364| getReceiver: [NilLiteral] nil @@ -608,19 +608,19 @@ control/loops.rb: # 22| getStmt: [AssignExpr] ... = ... # 22| getDesugared: [StmtSequence] ... # 22| getStmt: [AssignExpr] ... = ... +# 22| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1 # 22| getAnOperand/getRightOperand: [SplatExpr] * ... # 22| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] __synth__0__1 -# 22| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1 # 22| getStmt: [AssignExpr] ... = ... # 22| getAnOperand/getLeftOperand: [LocalVariableAccess] key # 22| getAnOperand/getRightOperand: [MethodCall] call to [] -# 22| getArgument: [IntegerLiteral] 0 # 22| getReceiver: [LocalVariableAccess] __synth__0__1 +# 22| getArgument: [IntegerLiteral] 0 # 22| getStmt: [AssignExpr] ... = ... # 22| getAnOperand/getLeftOperand: [LocalVariableAccess] value # 22| getAnOperand/getRightOperand: [MethodCall] call to [] -# 22| getArgument: [IntegerLiteral] 1 # 22| getReceiver: [LocalVariableAccess] __synth__0__1 +# 22| getArgument: [IntegerLiteral] 1 # 22| getAnOperand/getLeftOperand: [DestructuredLhsExpr] (..., ...) # 23| getStmt: [AssignAddExpr] ... += ... # 23| getDesugared: [AssignExpr] ... = ... @@ -653,19 +653,19 @@ control/loops.rb: # 28| getStmt: [AssignExpr] ... = ... # 28| getDesugared: [StmtSequence] ... # 28| getStmt: [AssignExpr] ... = ... +# 28| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1 # 28| getAnOperand/getRightOperand: [SplatExpr] * ... # 28| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] __synth__0__1 -# 28| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1 # 28| getStmt: [AssignExpr] ... = ... # 28| getAnOperand/getLeftOperand: [LocalVariableAccess] key # 28| getAnOperand/getRightOperand: [MethodCall] call to [] -# 28| getArgument: [IntegerLiteral] 0 # 28| getReceiver: [LocalVariableAccess] __synth__0__1 +# 28| getArgument: [IntegerLiteral] 0 # 28| getStmt: [AssignExpr] ... = ... # 28| getAnOperand/getLeftOperand: [LocalVariableAccess] value # 28| getAnOperand/getRightOperand: [MethodCall] call to [] -# 28| getArgument: [IntegerLiteral] 1 # 28| getReceiver: [LocalVariableAccess] __synth__0__1 +# 28| getArgument: [IntegerLiteral] 1 # 28| getAnOperand/getLeftOperand: [DestructuredLhsExpr] (..., ...) # 29| getStmt: [AssignAddExpr] ... += ... # 29| getDesugared: [AssignExpr] ... = ... From 3d0c23e441683bdb4fa21ead7f894d69ca2f4c7a Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Wed, 3 Aug 2022 09:52:11 +0200 Subject: [PATCH 411/505] Python: Accept `.expected` for TarSlip Changed after merging https://github.com/github/codeql/pull/9579, which improved our handling of `not` for guards. --- .../query-tests/Security/CWE-022-TarSlip/TarSlip.expected | 6 ------ 1 file changed, 6 deletions(-) diff --git a/python/ql/test/query-tests/Security/CWE-022-TarSlip/TarSlip.expected b/python/ql/test/query-tests/Security/CWE-022-TarSlip/TarSlip.expected index 2ddfe7143d0..3cd40605b96 100644 --- a/python/ql/test/query-tests/Security/CWE-022-TarSlip/TarSlip.expected +++ b/python/ql/test/query-tests/Security/CWE-022-TarSlip/TarSlip.expected @@ -7,8 +7,6 @@ edges | tarslip.py:40:7:40:39 | ControlFlowNode for Attribute() | tarslip.py:41:24:41:26 | ControlFlowNode for tar | | tarslip.py:56:7:56:39 | ControlFlowNode for Attribute() | tarslip.py:57:5:57:9 | GSSA Variable entry | | tarslip.py:57:5:57:9 | GSSA Variable entry | tarslip.py:59:21:59:25 | ControlFlowNode for entry | -| tarslip.py:79:7:79:39 | ControlFlowNode for Attribute() | tarslip.py:80:5:80:9 | GSSA Variable entry | -| tarslip.py:80:5:80:9 | GSSA Variable entry | tarslip.py:82:21:82:25 | ControlFlowNode for entry | nodes | tarslip.py:12:7:12:39 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | | tarslip.py:13:1:13:3 | ControlFlowNode for tar | semmle.label | ControlFlowNode for tar | @@ -23,9 +21,6 @@ nodes | tarslip.py:56:7:56:39 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | | tarslip.py:57:5:57:9 | GSSA Variable entry | semmle.label | GSSA Variable entry | | tarslip.py:59:21:59:25 | ControlFlowNode for entry | semmle.label | ControlFlowNode for entry | -| tarslip.py:79:7:79:39 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| tarslip.py:80:5:80:9 | GSSA Variable entry | semmle.label | GSSA Variable entry | -| tarslip.py:82:21:82:25 | ControlFlowNode for entry | semmle.label | ControlFlowNode for entry | subpaths #select | tarslip.py:13:1:13:3 | ControlFlowNode for tar | tarslip.py:12:7:12:39 | ControlFlowNode for Attribute() | tarslip.py:13:1:13:3 | ControlFlowNode for tar | Extraction of tarfile from $@ | tarslip.py:12:7:12:39 | ControlFlowNode for Attribute() | a potentially untrusted source | @@ -33,4 +28,3 @@ subpaths | tarslip.py:37:17:37:21 | ControlFlowNode for entry | tarslip.py:33:7:33:39 | ControlFlowNode for Attribute() | tarslip.py:37:17:37:21 | ControlFlowNode for entry | Extraction of tarfile from $@ | tarslip.py:33:7:33:39 | ControlFlowNode for Attribute() | a potentially untrusted source | | tarslip.py:41:24:41:26 | ControlFlowNode for tar | tarslip.py:40:7:40:39 | ControlFlowNode for Attribute() | tarslip.py:41:24:41:26 | ControlFlowNode for tar | Extraction of tarfile from $@ | tarslip.py:40:7:40:39 | ControlFlowNode for Attribute() | a potentially untrusted source | | tarslip.py:59:21:59:25 | ControlFlowNode for entry | tarslip.py:56:7:56:39 | ControlFlowNode for Attribute() | tarslip.py:59:21:59:25 | ControlFlowNode for entry | Extraction of tarfile from $@ | tarslip.py:56:7:56:39 | ControlFlowNode for Attribute() | a potentially untrusted source | -| tarslip.py:82:21:82:25 | ControlFlowNode for entry | tarslip.py:79:7:79:39 | ControlFlowNode for Attribute() | tarslip.py:82:21:82:25 | ControlFlowNode for entry | Extraction of tarfile from $@ | tarslip.py:79:7:79:39 | ControlFlowNode for Attribute() | a potentially untrusted source | From 83498f58db85299f882afd12e61b0d2208c8ac38 Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Wed, 3 Aug 2022 08:53:43 +0100 Subject: [PATCH 412/505] Add missing import --- java/ql/src/Security/CWE/CWE-022/TaintedPath.ql | 1 + 1 file changed, 1 insertion(+) diff --git a/java/ql/src/Security/CWE/CWE-022/TaintedPath.ql b/java/ql/src/Security/CWE/CWE-022/TaintedPath.ql index 8743673eea7..05ca12f6537 100644 --- a/java/ql/src/Security/CWE/CWE-022/TaintedPath.ql +++ b/java/ql/src/Security/CWE/CWE-022/TaintedPath.ql @@ -15,6 +15,7 @@ import java import semmle.code.java.dataflow.FlowSources +private import semmle.code.java.dataflow.ExternalFlow import semmle.code.java.security.PathCreation import DataFlow::PathGraph import TaintedPathCommon From 2d76d6d51ab70537e35fb4c1dca1e0b64b4c6feb Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 26 Jul 2022 10:14:13 +0100 Subject: [PATCH 413/505] Swift: Tests for CWE-95. --- .../CWE-095/UnsafeWebViewFetch.expected | 1 + .../Security/CWE-095/UnsafeWebViewFetch.qlref | 1 + .../Security/CWE-095/UnsafeWebViewFetch.swift | 206 ++++++++++++++++++ 3 files changed, 208 insertions(+) create mode 100644 swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.expected create mode 100644 swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.qlref create mode 100644 swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.swift diff --git a/swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.expected b/swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.expected new file mode 100644 index 00000000000..583b1a97a98 --- /dev/null +++ b/swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.expected @@ -0,0 +1 @@ +| TODO | diff --git a/swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.qlref b/swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.qlref new file mode 100644 index 00000000000..ddd18844328 --- /dev/null +++ b/swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.qlref @@ -0,0 +1 @@ +queries/Security/CWE-095/UnsafeWebviewFetch.ql \ No newline at end of file diff --git a/swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.swift b/swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.swift new file mode 100644 index 00000000000..8b3fbcbe1d9 --- /dev/null +++ b/swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.swift @@ -0,0 +1,206 @@ + +// --- stubs --- + +class NSObject +{ +} + +class URL +{ + init?(string: String) {} + init?(string: String, relativeTo: URL?) {} +} + +extension String { + init(contentsOf: URL) throws { + var data = "" + + // ... + + self.init(data) + } +} + +class NSURLRequest : NSObject +{ + enum CachePolicy : UInt + { + case useProtocolCachePolicy + } +} + +typealias TimeInterval = Double + +class URLRequest +{ + typealias CachePolicy = NSURLRequest.CachePolicy + + init(url: URL, cachePolicy: URLRequest.CachePolicy = .useProtocolCachePolicy, timeoutInterval: TimeInterval = 60.0) {} +} + +class Data +{ + init(_ elements: S) {} +} + +class WKNavigation : NSObject +{ +} + +class UIResponder : NSObject +{ +} + +class UIView : UIResponder +{ +} + +class UIWebView : UIView +{ + func loadRequest(_ request: URLRequest) {} // deprecated + + func load(_ data: Data, mimeType MIMEType: String, textEncodingName: String, baseURL: URL) {} // deprecated + + func loadHTMLString(_ string: String, baseURL: URL?) {} // deprecated +} + +class WKWebView : UIView +{ + func load(_ request: URLRequest) -> WKNavigation? { + // ... + + return WKNavigation() + } + + func load(_ data: Data, mimeType MIMEType: String, characterEncodingName: String, baseURL: URL) -> WKNavigation? { + // ... + + return WKNavigation() + } + + func loadHTMLString(_ string: String, baseURL: URL?) -> WKNavigation? { + // ... + + return WKNavigation() + } +} + +// --- tests --- + +func getRemoteData() -> String { + let url = URL(string: "http://example.com/") + do + { + return try String(contentsOf: url!) + } catch { + return "" + } +} + +func testSimpleFlows() { + let webview = UIWebView() + + webview.loadHTMLString(try! String(contentsOf: URL(string: "http://example.com/")!), baseURL: nil) // BAD [NOT DETECTED] + + let data = try! String(contentsOf: URL(string: "http://example.com/")!) + webview.loadHTMLString(data, baseURL: nil) // BAD [NOT DETECTED] + + let url = URL(string: "http://example.com/") + webview.loadHTMLString(try! String(contentsOf: url!), baseURL: nil) // BAD [NOT DETECTED] +} + +func testUIWebView() { + let webview = UIWebView() + + let localString = "

    Local HTML

    " + let localStringFragment = "

    Local HTML

    " + let remoteString = getRemoteData() + + webview.loadHTMLString(localString, baseURL: nil) // GOOD: the HTML data is local + webview.loadHTMLString(getRemoteData(), baseURL: nil) // BAD: HTML contains remote input, may access local secrets [NOT DETECTED] + webview.loadHTMLString(remoteString, baseURL: nil) // BAD [NOT DETECTED] + + webview.loadHTMLString("" + localStringFragment + "", baseURL: nil) // GOOD: the HTML data is local + webview.loadHTMLString("" + remoteString + "", baseURL: nil) // BAD [NOT DETECTED] + + webview.loadHTMLString("\(localStringFragment)", baseURL: nil) // GOOD: the HTML data is local + webview.loadHTMLString("\(remoteString)", baseURL: nil) // BAD [NOT DETECTED] + + let localSafeURL = URL(string: "about:blank") + let localURL = URL(string: "http://example.com/") + let remoteURL = URL(string: remoteString) + let remoteURL2 = URL(string: "/path", relativeTo: remoteURL) + + webview.loadHTMLString(localString, baseURL: localSafeURL!) // GOOD: a safe baseURL is specified + webview.loadHTMLString(remoteString, baseURL: localSafeURL!) // GOOD: a safe baseURL is specified + webview.loadHTMLString(localString, baseURL: localURL!) // GOOD: a presumed safe baseURL is specified + webview.loadHTMLString(remoteString, baseURL: localURL!) // GOOD: a presumed safe baseURL is specified + webview.loadHTMLString(localString, baseURL: remoteURL!) // GOOD: the HTML data is local + webview.loadHTMLString(remoteString, baseURL: remoteURL!) // BAD [NOT DETECTED] + webview.loadHTMLString(localString, baseURL: remoteURL2!) // GOOD: the HTML data is local + webview.loadHTMLString(remoteString, baseURL: remoteURL2!) // BAD [NOT DETECTED] + + let localRequest = URLRequest(url: localURL!) + let remoteRequest = URLRequest(url: remoteURL!) + + webview.loadRequest(localRequest) // GOOD: loadRequest is out of scope as it has no baseURL + webview.loadRequest(remoteRequest) // GOOD: loadRequest is out of scope as it has no baseURL + + let localData = Data(localString.utf8) + let remoteData = Data(remoteString.utf8) + webview.load(localData, mimeType: "text/html", textEncodingName: "utf-8", baseURL: localSafeURL!) // GOOD: the data is local + webview.load(remoteData, mimeType: "text/html", textEncodingName: "utf-8", baseURL: localSafeURL!) // GOOD: a safe baseURL is specified + webview.load(localData, mimeType: "text/html", textEncodingName: "utf-8", baseURL: remoteURL!) // GOOD: the HTML data is local + webview.load(remoteData, mimeType: "text/html", textEncodingName: "utf-8", baseURL: remoteURL!) // BAD [NOT DETECTED] +} + +func testWKWebView() { + let webview = WKWebView() + // note: `WKWebView` is safer than `UIWebView` as it has better security configuration options + // and is more locked down by default. + + let localString = "

    Local HTML

    " + let localStringFragment = "

    Local HTML

    " + let remoteString = getRemoteData() + + webview.loadHTMLString(localString, baseURL: nil) // GOOD: the HTML data is local + webview.loadHTMLString(getRemoteData(), baseURL: nil) // BAD [NOT DETECTED] + webview.loadHTMLString(remoteString, baseURL: nil) // BAD [NOT DETECTED] + + webview.loadHTMLString("" + localStringFragment + "", baseURL: nil) // GOOD: the HTML data is local + webview.loadHTMLString("" + remoteString + "", baseURL: nil) // BAD [NOT DETECTED] + + webview.loadHTMLString("\(localStringFragment)", baseURL: nil) // GOOD: the HTML data is local + webview.loadHTMLString("\(remoteString)", baseURL: nil) // BAD [NOT DETECTED] + + let localSafeURL = URL(string: "about:blank") + let localURL = URL(string: "http://example.com/") + let remoteURL = URL(string: remoteString) + let remoteURL2 = URL(string: "/path", relativeTo: remoteURL) + + webview.loadHTMLString(localString, baseURL: localSafeURL!) // GOOD: a safe baseURL is specified + webview.loadHTMLString(remoteString, baseURL: localSafeURL!) // GOOD: a safe baseURL is specified + webview.loadHTMLString(localString, baseURL: localURL!) // GOOD: a presumed safe baseURL is specified + webview.loadHTMLString(remoteString, baseURL: localURL!) // GOOD: a presumed safe baseURL is specified + webview.loadHTMLString(localString, baseURL: remoteURL!) // GOOD: the HTML data is local + webview.loadHTMLString(remoteString, baseURL: remoteURL!) // BAD [NOT DETECTED] + webview.loadHTMLString(localString, baseURL: remoteURL2!) // GOOD: the HTML data is local + webview.loadHTMLString(remoteString, baseURL: remoteURL2!) // BAD [NOT DETECTED] + + let localRequest = URLRequest(url: localURL!) + let remoteRequest = URLRequest(url: remoteURL!) + + webview.load(localRequest) // GOOD: loadRequest is out of scope as it has no baseURL + webview.load(remoteRequest) // GOOD: loadRequest is out of scope as it has no baseURL + + let localData = Data(localString.utf8) + let remoteData = Data(remoteString.utf8) + webview.load(localData, mimeType: "text/html", characterEncodingName: "utf-8", baseURL: localSafeURL!) // GOOD: the data is local + webview.load(remoteData, mimeType: "text/html", characterEncodingName: "utf-8", baseURL: localSafeURL!) // GOOD: a safe baseURL is specified + webview.load(localData, mimeType: "text/html", characterEncodingName: "utf-8", baseURL: remoteURL!) // GOOD: the HTML data is local + webview.load(remoteData, mimeType: "text/html", characterEncodingName: "utf-8", baseURL: remoteURL!) // BAD [NOT DETECTED] +} + +testSimpleFlows() +testUIWebView() +testWKWebView() From e04c77ce158f8387e67e687355193e8125c9b14f Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Wed, 3 Aug 2022 09:37:20 +0100 Subject: [PATCH 414/505] Rename sanitizer --- go/ql/lib/semmle/go/security/TaintedPathCustomizations.qll | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/go/ql/lib/semmle/go/security/TaintedPathCustomizations.qll b/go/ql/lib/semmle/go/security/TaintedPathCustomizations.qll index 5bdba0ea931..61499340de3 100644 --- a/go/ql/lib/semmle/go/security/TaintedPathCustomizations.qll +++ b/go/ql/lib/semmle/go/security/TaintedPathCustomizations.qll @@ -71,10 +71,10 @@ module TaintedPath { } /** - * A numeric-typed node, considered a sanitizer for path traversal. + * A numeric- or boolean-typed node, considered a sanitizer for path traversal. */ - class NumericSanitizer extends Sanitizer { - NumericSanitizer() { + class NumericOrBooleanSanitizer extends Sanitizer { + NumericOrBooleanSanitizer() { this.getType() instanceof NumericType or this.getType() instanceof BoolType } } From 53ea65b0459fc262b208bb3a756f9aec018b51f1 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 27 Jul 2022 14:43:03 +0100 Subject: [PATCH 415/505] Swift: Implement query. --- .../Security/CWE-095/UnsafeWebViewFetch.ql | 115 +++++++++++++++++- .../CWE-095/UnsafeWebViewFetch.expected | 46 ++++++- .../Security/CWE-095/UnsafeWebViewFetch.swift | 12 +- 3 files changed, 164 insertions(+), 9 deletions(-) diff --git a/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetch.ql b/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetch.ql index 2c047db4906..bb8d64a237b 100644 --- a/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetch.ql +++ b/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetch.ql @@ -1,7 +1,7 @@ /** * @name Unsafe WebView fetch * @description TODO - * @kind problem + * @kind path-problem * @problem.severity warning * @security-severity TODO * @precision high @@ -13,5 +13,116 @@ */ import swift +import codeql.swift.dataflow.DataFlow +import codeql.swift.dataflow.TaintTracking +import codeql.swift.dataflow.FlowSources +import DataFlow::PathGraph +import codeql.swift.frameworks.StandardLibrary.String -select "TODO" +/** + * A taint source that is `String(contentsOf:)`. + * TODO: this shouldn't be needed when `StringSource` in `String.qll` is working. + */ +class StringContentsOfURLSource extends RemoteFlowSource { + StringContentsOfURLSource() { + exists(CallExpr call, AbstractFunctionDecl f | + call.getFunction().(ApplyExpr).getStaticTarget() = f and + f.getName() = "init(contentsOf:)" and + f.getParam(0).getType().getName() = "URL" and + this.asExpr() = call + ) + } + + override string getSourceType() { result = "" } +} + +/** + * A sink that is a candidate result for this query, such as certain arguments + * to `UIWebView.loadHTMLString`. + */ +class Sink extends DataFlow::Node { + Expr baseURL; + + Sink() { + exists( + AbstractFunctionDecl funcDecl, CallExpr call, string funcName, string paramName, int arg, + int baseURLarg + | + // arguments to method calls... + exists(string className, ClassDecl c | + ( + // `loadHTMLString` + className = ["UIWebView", "WKWebView"] and + funcName = "loadHTMLString(_:baseURL:)" and + paramName = "string" + or + // `UIWebView.load` + className = "UIWebView" and + funcName = "load(_:mimeType:textEncodingName:baseURL:)" and + paramName = "data" + or + // `WKWebView.load` + className = "WKWebView" and + funcName = "load(_:mimeType:characterEncodingName:baseURL:)" and + paramName = "data" + ) and + c.getName() = className and + c.getAMember() = funcDecl and + call.getFunction().(ApplyExpr).getStaticTarget() = funcDecl + ) and + // match up `funcName`, `paramName`, `arg`, `node`. + funcDecl.getName() = funcName and + funcDecl.getParam(pragma[only_bind_into](arg)).getName() = paramName and + call.getArgument(pragma[only_bind_into](arg)).getExpr() = this.asExpr() and + // match up `baseURLArg` + funcDecl.getParam(pragma[only_bind_into](baseURLarg)).getName() = "baseURL" and + call.getArgument(pragma[only_bind_into](baseURLarg)).getExpr() = baseURL + ) + } + + /** + * Gets the `baseURL` argument associated with this sink. + */ + Expr getBaseURL() { result = baseURL } +} + +/** + * Taint configuration from taint sources to sinks (and `baseURL` arguments) + * for this query. + */ +class UnsafeWebViewFetchConfig extends TaintTracking::Configuration { + UnsafeWebViewFetchConfig() { this = "UnsafeWebViewFetchConfig" } + + override predicate isSource(DataFlow::Node node) { node instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node node) { + node instanceof Sink + } + + override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { + // allow flow through `try!` and similar constructs + // TODO: this should probably be part of DataFlow / TaintTracking. + node1.asExpr() = node2.asExpr().(AnyTryExpr).getSubExpr() + or + // allow flow through `!` + // TODO: this should probably be part of DataFlow / TaintTracking. + node1.asExpr() = node2.asExpr().(ForceValueExpr).getSubExpr() + or + // allow flow through string concatenation. + // TODO: this should probably be part of TaintTracking. + node2.asExpr().(AddExpr).getAnOperand() = node1.asExpr() + } +} + +from + UnsafeWebViewFetchConfig config, DataFlow::PathNode sourceNode, DataFlow::PathNode sinkNode, + Sink sink, string message +where + config.hasFlowPath(sourceNode, sinkNode) and + sink = sinkNode.getNode() and + ( + // base URL is nil + sink.getBaseURL() instanceof NilLiteralExpr and + message = "Tainted data is used in a WebView fetch without restricting the base URL." + ) +select sinkNode, sourceNode, sinkNode, message diff --git a/swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.expected b/swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.expected index 583b1a97a98..ae981531fae 100644 --- a/swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.expected +++ b/swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.expected @@ -1 +1,45 @@ -| TODO | +edges +| UnsafeWebViewFetch.swift:94:10:94:37 | try ... : | UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | +| UnsafeWebViewFetch.swift:94:10:94:37 | try ... : | UnsafeWebViewFetch.swift:120:25:120:39 | call to getRemoteData() | +| UnsafeWebViewFetch.swift:94:10:94:37 | try ... : | UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | +| UnsafeWebViewFetch.swift:94:10:94:37 | try ... : | UnsafeWebViewFetch.swift:167:25:167:39 | call to getRemoteData() | +| UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:94:10:94:37 | try ... : | +| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:121:25:121:25 | remoteString | +| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:124:25:124:51 | ... call to +(_:_:) ... | +| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:135:25:135:25 | remoteString | +| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:137:25:137:25 | remoteString | +| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:139:25:139:25 | remoteString | +| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:141:25:141:25 | remoteString | +| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:168:25:168:25 | remoteString | +| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:171:25:171:51 | ... call to +(_:_:) ... | +| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:182:25:182:25 | remoteString | +| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:184:25:184:25 | remoteString | +| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:186:25:186:25 | remoteString | +| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:188:25:188:25 | remoteString | +nodes +| UnsafeWebViewFetch.swift:94:10:94:37 | try ... : | semmle.label | try ... : | +| UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | semmle.label | call to ... : | +| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | semmle.label | call to getRemoteData() : | +| UnsafeWebViewFetch.swift:120:25:120:39 | call to getRemoteData() | semmle.label | call to getRemoteData() | +| UnsafeWebViewFetch.swift:121:25:121:25 | remoteString | semmle.label | remoteString | +| UnsafeWebViewFetch.swift:124:25:124:51 | ... call to +(_:_:) ... | semmle.label | ... call to +(_:_:) ... | +| UnsafeWebViewFetch.swift:135:25:135:25 | remoteString | semmle.label | remoteString | +| UnsafeWebViewFetch.swift:137:25:137:25 | remoteString | semmle.label | remoteString | +| UnsafeWebViewFetch.swift:139:25:139:25 | remoteString | semmle.label | remoteString | +| UnsafeWebViewFetch.swift:141:25:141:25 | remoteString | semmle.label | remoteString | +| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | semmle.label | call to getRemoteData() : | +| UnsafeWebViewFetch.swift:167:25:167:39 | call to getRemoteData() | semmle.label | call to getRemoteData() | +| UnsafeWebViewFetch.swift:168:25:168:25 | remoteString | semmle.label | remoteString | +| UnsafeWebViewFetch.swift:171:25:171:51 | ... call to +(_:_:) ... | semmle.label | ... call to +(_:_:) ... | +| UnsafeWebViewFetch.swift:182:25:182:25 | remoteString | semmle.label | remoteString | +| UnsafeWebViewFetch.swift:184:25:184:25 | remoteString | semmle.label | remoteString | +| UnsafeWebViewFetch.swift:186:25:186:25 | remoteString | semmle.label | remoteString | +| UnsafeWebViewFetch.swift:188:25:188:25 | remoteString | semmle.label | remoteString | +subpaths +#select +| UnsafeWebViewFetch.swift:120:25:120:39 | call to getRemoteData() | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:120:25:120:39 | call to getRemoteData() | Tainted data is used in a WebView fetch without restricting the base URL. | +| UnsafeWebViewFetch.swift:121:25:121:25 | remoteString | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:121:25:121:25 | remoteString | Tainted data is used in a WebView fetch without restricting the base URL. | +| UnsafeWebViewFetch.swift:124:25:124:51 | ... call to +(_:_:) ... | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:124:25:124:51 | ... call to +(_:_:) ... | Tainted data is used in a WebView fetch without restricting the base URL. | +| UnsafeWebViewFetch.swift:167:25:167:39 | call to getRemoteData() | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:167:25:167:39 | call to getRemoteData() | Tainted data is used in a WebView fetch without restricting the base URL. | +| UnsafeWebViewFetch.swift:168:25:168:25 | remoteString | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:168:25:168:25 | remoteString | Tainted data is used in a WebView fetch without restricting the base URL. | +| UnsafeWebViewFetch.swift:171:25:171:51 | ... call to +(_:_:) ... | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:171:25:171:51 | ... call to +(_:_:) ... | Tainted data is used in a WebView fetch without restricting the base URL. | diff --git a/swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.swift b/swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.swift index 8b3fbcbe1d9..f6c1fd80b24 100644 --- a/swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.swift +++ b/swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.swift @@ -117,11 +117,11 @@ func testUIWebView() { let remoteString = getRemoteData() webview.loadHTMLString(localString, baseURL: nil) // GOOD: the HTML data is local - webview.loadHTMLString(getRemoteData(), baseURL: nil) // BAD: HTML contains remote input, may access local secrets [NOT DETECTED] - webview.loadHTMLString(remoteString, baseURL: nil) // BAD [NOT DETECTED] + webview.loadHTMLString(getRemoteData(), baseURL: nil) // BAD: HTML contains remote input, may access local secrets + webview.loadHTMLString(remoteString, baseURL: nil) // BAD webview.loadHTMLString("" + localStringFragment + "", baseURL: nil) // GOOD: the HTML data is local - webview.loadHTMLString("" + remoteString + "", baseURL: nil) // BAD [NOT DETECTED] + webview.loadHTMLString("" + remoteString + "", baseURL: nil) // BAD webview.loadHTMLString("\(localStringFragment)", baseURL: nil) // GOOD: the HTML data is local webview.loadHTMLString("\(remoteString)", baseURL: nil) // BAD [NOT DETECTED] @@ -164,11 +164,11 @@ func testWKWebView() { let remoteString = getRemoteData() webview.loadHTMLString(localString, baseURL: nil) // GOOD: the HTML data is local - webview.loadHTMLString(getRemoteData(), baseURL: nil) // BAD [NOT DETECTED] - webview.loadHTMLString(remoteString, baseURL: nil) // BAD [NOT DETECTED] + webview.loadHTMLString(getRemoteData(), baseURL: nil) // BAD + webview.loadHTMLString(remoteString, baseURL: nil) // BAD webview.loadHTMLString("" + localStringFragment + "", baseURL: nil) // GOOD: the HTML data is local - webview.loadHTMLString("" + remoteString + "", baseURL: nil) // BAD [NOT DETECTED] + webview.loadHTMLString("" + remoteString + "", baseURL: nil) // BAD webview.loadHTMLString("\(localStringFragment)", baseURL: nil) // GOOD: the HTML data is local webview.loadHTMLString("\(remoteString)", baseURL: nil) // BAD [NOT DETECTED] From 651b73e21ec637ae5e9141964894581cf427a4e1 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 3 Aug 2022 09:17:34 +0100 Subject: [PATCH 416/505] Swift: Check for tainted baseURL. --- .../Security/CWE-095/UnsafeWebViewFetch.ql | 17 ++++++++++- .../CWE-095/UnsafeWebViewFetch.expected | 28 +++++++++++++++++++ .../Security/CWE-095/UnsafeWebViewFetch.swift | 8 +++--- 3 files changed, 48 insertions(+), 5 deletions(-) diff --git a/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetch.ql b/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetch.ql index bb8d64a237b..fe48ca8ec04 100644 --- a/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetch.ql +++ b/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetch.ql @@ -96,7 +96,8 @@ class UnsafeWebViewFetchConfig extends TaintTracking::Configuration { override predicate isSource(DataFlow::Node node) { node instanceof RemoteFlowSource } override predicate isSink(DataFlow::Node node) { - node instanceof Sink + node instanceof Sink or + node.asExpr() = any(Sink s).getBaseURL() } override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { @@ -111,6 +112,16 @@ class UnsafeWebViewFetchConfig extends TaintTracking::Configuration { // allow flow through string concatenation. // TODO: this should probably be part of TaintTracking. node2.asExpr().(AddExpr).getAnOperand() = node1.asExpr() + or + // allow flow through `URL.init`. + exists(CallExpr call, ClassDecl c, AbstractFunctionDecl f | + c.getName() = "URL" and + c.getAMember() = f and + f.getName() = ["init(string:)", "init(string:relativeTo:)"] and + call.getFunction().(ApplyExpr).getStaticTarget() = f and + node1.asExpr() = call.getArgument(_).getExpr() and + node2.asExpr() = call + ) } } @@ -124,5 +135,9 @@ where // base URL is nil sink.getBaseURL() instanceof NilLiteralExpr and message = "Tainted data is used in a WebView fetch without restricting the base URL." + or + // base URL is tainted + config.hasFlow(_, any(DataFlow::Node n | n.asExpr() = sink.getBaseURL())) and + message = "Tainted data is used in a WebView fetch with a tainted base URL." ) select sinkNode, sourceNode, sinkNode, message diff --git a/swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.expected b/swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.expected index ae981531fae..9d3ef5ef4f0 100644 --- a/swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.expected +++ b/swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.expected @@ -8,14 +8,26 @@ edges | UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:124:25:124:51 | ... call to +(_:_:) ... | | UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:135:25:135:25 | remoteString | | UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:137:25:137:25 | remoteString | +| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:138:47:138:56 | ...! | | UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:139:25:139:25 | remoteString | +| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:139:48:139:57 | ...! | +| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:140:47:140:57 | ...! | | UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:141:25:141:25 | remoteString | +| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:141:48:141:58 | ...! | +| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:153:85:153:94 | ...! | +| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:154:86:154:95 | ...! | | UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:168:25:168:25 | remoteString | | UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:171:25:171:51 | ... call to +(_:_:) ... | | UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:182:25:182:25 | remoteString | | UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:184:25:184:25 | remoteString | +| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:185:47:185:56 | ...! | | UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:186:25:186:25 | remoteString | +| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:186:48:186:57 | ...! | +| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:187:47:187:57 | ...! | | UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:188:25:188:25 | remoteString | +| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:188:48:188:58 | ...! | +| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:200:90:200:99 | ...! | +| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:201:91:201:100 | ...! | nodes | UnsafeWebViewFetch.swift:94:10:94:37 | try ... : | semmle.label | try ... : | | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | semmle.label | call to ... : | @@ -25,21 +37,37 @@ nodes | UnsafeWebViewFetch.swift:124:25:124:51 | ... call to +(_:_:) ... | semmle.label | ... call to +(_:_:) ... | | UnsafeWebViewFetch.swift:135:25:135:25 | remoteString | semmle.label | remoteString | | UnsafeWebViewFetch.swift:137:25:137:25 | remoteString | semmle.label | remoteString | +| UnsafeWebViewFetch.swift:138:47:138:56 | ...! | semmle.label | ...! | | UnsafeWebViewFetch.swift:139:25:139:25 | remoteString | semmle.label | remoteString | +| UnsafeWebViewFetch.swift:139:48:139:57 | ...! | semmle.label | ...! | +| UnsafeWebViewFetch.swift:140:47:140:57 | ...! | semmle.label | ...! | | UnsafeWebViewFetch.swift:141:25:141:25 | remoteString | semmle.label | remoteString | +| UnsafeWebViewFetch.swift:141:48:141:58 | ...! | semmle.label | ...! | +| UnsafeWebViewFetch.swift:153:85:153:94 | ...! | semmle.label | ...! | +| UnsafeWebViewFetch.swift:154:86:154:95 | ...! | semmle.label | ...! | | UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | semmle.label | call to getRemoteData() : | | UnsafeWebViewFetch.swift:167:25:167:39 | call to getRemoteData() | semmle.label | call to getRemoteData() | | UnsafeWebViewFetch.swift:168:25:168:25 | remoteString | semmle.label | remoteString | | UnsafeWebViewFetch.swift:171:25:171:51 | ... call to +(_:_:) ... | semmle.label | ... call to +(_:_:) ... | | UnsafeWebViewFetch.swift:182:25:182:25 | remoteString | semmle.label | remoteString | | UnsafeWebViewFetch.swift:184:25:184:25 | remoteString | semmle.label | remoteString | +| UnsafeWebViewFetch.swift:185:47:185:56 | ...! | semmle.label | ...! | | UnsafeWebViewFetch.swift:186:25:186:25 | remoteString | semmle.label | remoteString | +| UnsafeWebViewFetch.swift:186:48:186:57 | ...! | semmle.label | ...! | +| UnsafeWebViewFetch.swift:187:47:187:57 | ...! | semmle.label | ...! | | UnsafeWebViewFetch.swift:188:25:188:25 | remoteString | semmle.label | remoteString | +| UnsafeWebViewFetch.swift:188:48:188:58 | ...! | semmle.label | ...! | +| UnsafeWebViewFetch.swift:200:90:200:99 | ...! | semmle.label | ...! | +| UnsafeWebViewFetch.swift:201:91:201:100 | ...! | semmle.label | ...! | subpaths #select | UnsafeWebViewFetch.swift:120:25:120:39 | call to getRemoteData() | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:120:25:120:39 | call to getRemoteData() | Tainted data is used in a WebView fetch without restricting the base URL. | | UnsafeWebViewFetch.swift:121:25:121:25 | remoteString | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:121:25:121:25 | remoteString | Tainted data is used in a WebView fetch without restricting the base URL. | | UnsafeWebViewFetch.swift:124:25:124:51 | ... call to +(_:_:) ... | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:124:25:124:51 | ... call to +(_:_:) ... | Tainted data is used in a WebView fetch without restricting the base URL. | +| UnsafeWebViewFetch.swift:139:25:139:25 | remoteString | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:139:25:139:25 | remoteString | Tainted data is used in a WebView fetch with a tainted base URL. | +| UnsafeWebViewFetch.swift:141:25:141:25 | remoteString | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:141:25:141:25 | remoteString | Tainted data is used in a WebView fetch with a tainted base URL. | | UnsafeWebViewFetch.swift:167:25:167:39 | call to getRemoteData() | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:167:25:167:39 | call to getRemoteData() | Tainted data is used in a WebView fetch without restricting the base URL. | | UnsafeWebViewFetch.swift:168:25:168:25 | remoteString | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:168:25:168:25 | remoteString | Tainted data is used in a WebView fetch without restricting the base URL. | | UnsafeWebViewFetch.swift:171:25:171:51 | ... call to +(_:_:) ... | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:171:25:171:51 | ... call to +(_:_:) ... | Tainted data is used in a WebView fetch without restricting the base URL. | +| UnsafeWebViewFetch.swift:186:25:186:25 | remoteString | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:186:25:186:25 | remoteString | Tainted data is used in a WebView fetch with a tainted base URL. | +| UnsafeWebViewFetch.swift:188:25:188:25 | remoteString | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:188:25:188:25 | remoteString | Tainted data is used in a WebView fetch with a tainted base URL. | diff --git a/swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.swift b/swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.swift index f6c1fd80b24..3fc5aca878f 100644 --- a/swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.swift +++ b/swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.swift @@ -136,9 +136,9 @@ func testUIWebView() { webview.loadHTMLString(localString, baseURL: localURL!) // GOOD: a presumed safe baseURL is specified webview.loadHTMLString(remoteString, baseURL: localURL!) // GOOD: a presumed safe baseURL is specified webview.loadHTMLString(localString, baseURL: remoteURL!) // GOOD: the HTML data is local - webview.loadHTMLString(remoteString, baseURL: remoteURL!) // BAD [NOT DETECTED] + webview.loadHTMLString(remoteString, baseURL: remoteURL!) // BAD webview.loadHTMLString(localString, baseURL: remoteURL2!) // GOOD: the HTML data is local - webview.loadHTMLString(remoteString, baseURL: remoteURL2!) // BAD [NOT DETECTED] + webview.loadHTMLString(remoteString, baseURL: remoteURL2!) // BAD let localRequest = URLRequest(url: localURL!) let remoteRequest = URLRequest(url: remoteURL!) @@ -183,9 +183,9 @@ func testWKWebView() { webview.loadHTMLString(localString, baseURL: localURL!) // GOOD: a presumed safe baseURL is specified webview.loadHTMLString(remoteString, baseURL: localURL!) // GOOD: a presumed safe baseURL is specified webview.loadHTMLString(localString, baseURL: remoteURL!) // GOOD: the HTML data is local - webview.loadHTMLString(remoteString, baseURL: remoteURL!) // BAD [NOT DETECTED] + webview.loadHTMLString(remoteString, baseURL: remoteURL!) // BAD webview.loadHTMLString(localString, baseURL: remoteURL2!) // GOOD: the HTML data is local - webview.loadHTMLString(remoteString, baseURL: remoteURL2!) // BAD [NOT DETECTED] + webview.loadHTMLString(remoteString, baseURL: remoteURL2!) // BAD let localRequest = URLRequest(url: localURL!) let remoteRequest = URLRequest(url: remoteURL!) From ea17b852b46a0e3db1be1e9091f8e0ea3e5b6524 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 2 Aug 2022 14:45:16 +0100 Subject: [PATCH 417/505] Swift: Explain ExternalRemoteFlowSource. --- swift/ql/lib/codeql/swift/dataflow/FlowSources.qll | 3 +++ 1 file changed, 3 insertions(+) diff --git a/swift/ql/lib/codeql/swift/dataflow/FlowSources.qll b/swift/ql/lib/codeql/swift/dataflow/FlowSources.qll index 4ef8a28baa3..dc0d692dfc4 100644 --- a/swift/ql/lib/codeql/swift/dataflow/FlowSources.qll +++ b/swift/ql/lib/codeql/swift/dataflow/FlowSources.qll @@ -11,6 +11,9 @@ abstract class RemoteFlowSource extends Node { abstract string getSourceType(); } +/** + * A data flow source of remote user input that is defined through 'models as data'. + */ private class ExternalRemoteFlowSource extends RemoteFlowSource { ExternalRemoteFlowSource() { sourceNode(this, "remote") } From 8d9653a99960c39153a2beac1f75605485a092ea Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 3 Aug 2022 09:54:54 +0100 Subject: [PATCH 418/505] Swift: Generated security-severity tag. --- swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetch.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetch.ql b/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetch.ql index fe48ca8ec04..c8677f704a1 100644 --- a/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetch.ql +++ b/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetch.ql @@ -3,7 +3,7 @@ * @description TODO * @kind path-problem * @problem.severity warning - * @security-severity TODO + * @security-severity 6.1 * @precision high * @id swift/unsafe-webview-fetch * @tags security From 84a4b6a8663276fdeffd4b7addb6d3d6ac0c5667 Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Wed, 3 Aug 2022 10:42:09 +0100 Subject: [PATCH 419/505] Make reporting locations consistent with PathCreation; add test --- .../src/Security/CWE/CWE-022/TaintedPath.ql | 18 ++++++++++++++-- .../CWE-022/semmle/tests/TaintedPath.expected | 4 ++++ .../security/CWE-022/semmle/tests/Test.java | 21 ++++++++++++------- .../security/CWE-022/semmle/tests/options | 2 +- .../commons/io/output/LockableFileWriter.java | 7 +++++++ 5 files changed, 42 insertions(+), 10 deletions(-) create mode 100644 java/ql/test/stubs/apache-commons-io-2.6/org/apache/commons/io/output/LockableFileWriter.java diff --git a/java/ql/src/Security/CWE/CWE-022/TaintedPath.ql b/java/ql/src/Security/CWE/CWE-022/TaintedPath.ql index 05ca12f6537..671e9b00b4d 100644 --- a/java/ql/src/Security/CWE/CWE-022/TaintedPath.ql +++ b/java/ql/src/Security/CWE/CWE-022/TaintedPath.ql @@ -50,7 +50,21 @@ class TaintedPathConfig extends TaintTracking::Configuration { } } +/** + * Gets the data-flow node at which to report a path ending at `sink`. + * + * Previously this query flagged alerts exclusively at `PathCreation` sites, + * so to avoid perturbing existing alerts, where a `PathCreation` exists we + * continue to report there; otherwise we report directly at `sink`. + */ +DataFlow::Node getReportingNode(DataFlow::Node sink) { + any(TaintedPathConfig c).hasFlowTo(sink) and + if exists(PathCreation pc | pc.getAnInput() = sink.asExpr()) + then result.asExpr() = any(PathCreation pc | pc.getAnInput() = sink.asExpr()) + else result = sink +} + from DataFlow::PathNode source, DataFlow::PathNode sink, TaintedPathConfig conf where conf.hasFlowPath(source, sink) -select sink, source, sink, "$@ flows to here and is used in a path.", source.getNode(), - "User-provided value" +select getReportingNode(sink.getNode()), source, sink, "$@ flows to here and is used in a path.", + source.getNode(), "User-provided value" diff --git a/java/ql/test/query-tests/security/CWE-022/semmle/tests/TaintedPath.expected b/java/ql/test/query-tests/security/CWE-022/semmle/tests/TaintedPath.expected index 13ac840300d..830f4d76085 100644 --- a/java/ql/test/query-tests/security/CWE-022/semmle/tests/TaintedPath.expected +++ b/java/ql/test/query-tests/security/CWE-022/semmle/tests/TaintedPath.expected @@ -8,6 +8,7 @@ edges | Test.java:79:74:79:97 | getInputStream(...) : ServletInputStream | Test.java:79:52:79:98 | new InputStreamReader(...) : InputStreamReader | | Test.java:80:31:80:32 | br : BufferedReader | Test.java:80:31:80:43 | readLine(...) : String | | Test.java:80:31:80:43 | readLine(...) : String | Test.java:82:67:82:81 | ... + ... | +| Test.java:88:17:88:37 | getHostName(...) : String | Test.java:90:26:90:29 | temp | nodes | Test.java:19:18:19:38 | getHostName(...) : String | semmle.label | getHostName(...) : String | | Test.java:24:20:24:23 | temp | semmle.label | temp | @@ -20,6 +21,8 @@ nodes | Test.java:80:31:80:32 | br : BufferedReader | semmle.label | br : BufferedReader | | Test.java:80:31:80:43 | readLine(...) : String | semmle.label | readLine(...) : String | | Test.java:82:67:82:81 | ... + ... | semmle.label | ... + ... | +| Test.java:88:17:88:37 | getHostName(...) : String | semmle.label | getHostName(...) : String | +| Test.java:90:26:90:29 | temp | semmle.label | temp | subpaths #select | Test.java:24:11:24:24 | new File(...) | Test.java:19:18:19:38 | getHostName(...) : String | Test.java:24:20:24:23 | temp | $@ flows to here and is used in a path. | Test.java:19:18:19:38 | getHostName(...) | User-provided value | @@ -27,3 +30,4 @@ subpaths | Test.java:30:11:30:48 | getPath(...) | Test.java:19:18:19:38 | getHostName(...) : String | Test.java:30:44:30:47 | temp | $@ flows to here and is used in a path. | Test.java:19:18:19:38 | getHostName(...) | User-provided value | | Test.java:34:12:34:25 | new File(...) | Test.java:19:18:19:38 | getHostName(...) : String | Test.java:34:21:34:24 | temp | $@ flows to here and is used in a path. | Test.java:19:18:19:38 | getHostName(...) | User-provided value | | Test.java:82:52:82:88 | new FileWriter(...) | Test.java:79:74:79:97 | getInputStream(...) : ServletInputStream | Test.java:82:67:82:81 | ... + ... | $@ flows to here and is used in a path. | Test.java:79:74:79:97 | getInputStream(...) | User-provided value | +| Test.java:90:26:90:29 | temp | Test.java:88:17:88:37 | getHostName(...) : String | Test.java:90:26:90:29 | temp | $@ flows to here and is used in a path. | Test.java:88:17:88:37 | getHostName(...) | User-provided value | diff --git a/java/ql/test/query-tests/security/CWE-022/semmle/tests/Test.java b/java/ql/test/query-tests/security/CWE-022/semmle/tests/Test.java index a0a6694c061..f0d0147df08 100644 --- a/java/ql/test/query-tests/security/CWE-022/semmle/tests/Test.java +++ b/java/ql/test/query-tests/security/CWE-022/semmle/tests/Test.java @@ -2,7 +2,6 @@ // http://cwe.mitre.org/data/definitions/22.html package test.cwe22.semmle.tests; - import javax.servlet.http.*; import javax.servlet.ServletException; @@ -12,6 +11,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.FileSystems; +import org.apache.commons.io.output.LockableFileWriter; class Test { void doGet1(InetAddress address) @@ -19,13 +19,13 @@ class Test { String temp = address.getHostName(); File file; Path path; - + // BAD: construct a file path with user input file = new File(temp); - + // BAD: construct a path with user input path = Paths.get(temp); - + // BAD: construct a path with user input path = FileSystems.getDefault().getPath(temp); @@ -34,7 +34,7 @@ class Test { file = new File(temp); } } - + void doGet2(InetAddress address) throws IOException { String temp = address.getHostName(); @@ -44,7 +44,7 @@ class Test { if(isSafe(temp)) file = new File(temp); } - + void doGet3(InetAddress address) throws IOException { String temp = address.getHostName(); @@ -66,7 +66,7 @@ class Test { return false; return true; } - + boolean isSortOfSafe(String pathSpec) { // no file separators if (pathSpec.contains(File.separator)) @@ -82,4 +82,11 @@ class Test { BufferedWriter bw = new BufferedWriter(new FileWriter("dir/"+filename, true)); } } + + void doGet4(InetAddress address) + throws IOException { + String temp = address.getHostName(); + // BAD: open a file based on user input, using a MaD-documented API + new LockableFileWriter(temp); + } } diff --git a/java/ql/test/query-tests/security/CWE-022/semmle/tests/options b/java/ql/test/query-tests/security/CWE-022/semmle/tests/options index a41b28dc245..6f216f46554 100644 --- a/java/ql/test/query-tests/security/CWE-022/semmle/tests/options +++ b/java/ql/test/query-tests/security/CWE-022/semmle/tests/options @@ -1 +1 @@ -// semmle-extractor-options: --javac-args -cp ${testdir}/../../../../../stubs/servlet-api-2.4 +// semmle-extractor-options: --javac-args -cp ${testdir}/../../../../../stubs/servlet-api-2.4:${testdir}/../../../../../stubs/apache-commons-io-2.6 diff --git a/java/ql/test/stubs/apache-commons-io-2.6/org/apache/commons/io/output/LockableFileWriter.java b/java/ql/test/stubs/apache-commons-io-2.6/org/apache/commons/io/output/LockableFileWriter.java new file mode 100644 index 00000000000..3c7c9c5ebf5 --- /dev/null +++ b/java/ql/test/stubs/apache-commons-io-2.6/org/apache/commons/io/output/LockableFileWriter.java @@ -0,0 +1,7 @@ +package org.apache.commons.io.output; + +public class LockableFileWriter { + + public LockableFileWriter(String filename) { } + +} From 977823bd76d48b6af66f70795b1c0c559e116618 Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Wed, 3 Aug 2022 10:54:35 +0100 Subject: [PATCH 420/505] Create 2022-08-03-tainted-path-mad.md --- java/ql/src/change-notes/2022-08-03-tainted-path-mad.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 java/ql/src/change-notes/2022-08-03-tainted-path-mad.md diff --git a/java/ql/src/change-notes/2022-08-03-tainted-path-mad.md b/java/ql/src/change-notes/2022-08-03-tainted-path-mad.md new file mode 100644 index 00000000000..6f70a8f69e1 --- /dev/null +++ b/java/ql/src/change-notes/2022-08-03-tainted-path-mad.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* The query `java/path-injection` now recognises vulnerable APIs defined using the `SinkModelCsv` class with the `create-file` type. Out of the box this includes Apache Commons-IO functions, as well as any user-defined sinks. From 35f7fdf24b30e5a3f7e2dd6b515722b17e454b8d Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Wed, 3 Aug 2022 10:18:37 +0200 Subject: [PATCH 421/505] Update ruby/ql/lib/codeql/ruby/printAst.qll Co-authored-by: Tom Hvitved --- ruby/ql/lib/codeql/ruby/printAst.qll | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ruby/ql/lib/codeql/ruby/printAst.qll b/ruby/ql/lib/codeql/ruby/printAst.qll index d66e7fe0535..28f5def4969 100644 --- a/ruby/ql/lib/codeql/ruby/printAst.qll +++ b/ruby/ql/lib/codeql/ruby/printAst.qll @@ -36,6 +36,8 @@ private predicate shouldPrintAstEdge(AstNode parent, string edgeName, AstNode ch any(PrintAstConfiguration config).shouldPrintAstEdge(parent, edgeName, child) } +private int nonSynthIndex() { result = min([-1, any(int i | exists(getSynthChild(_, i)))]) - 1 } + newtype TPrintNode = TPrintRegularAstNode(AstNode n) { shouldPrintNode(n) } or TPrintRegExpNode(RE::RegExpTerm term) { @@ -114,7 +116,7 @@ class PrintRegularAstNode extends PrintAstNode, TPrintRegularAstNode { } private int getSynthAstNodeIndex() { - not astNode.isSynthesized() and result = -10 + not astNode.isSynthesized() and result = nonSynthIndex() or astNode = getSynthChild(astNode.getParent(), result) } From be7ba925f9f192db19143b71fe3cdc7e5d370180 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Wed, 3 Aug 2022 11:14:55 +0100 Subject: [PATCH 422/505] Swift: Cache 'lastRefRedef'. --- swift/ql/lib/codeql/swift/dataflow/Ssa.qll | 5 +++++ .../lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll | 3 +-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/swift/ql/lib/codeql/swift/dataflow/Ssa.qll b/swift/ql/lib/codeql/swift/dataflow/Ssa.qll index 2805dff4637..8f7e95b0caa 100644 --- a/swift/ql/lib/codeql/swift/dataflow/Ssa.qll +++ b/swift/ql/lib/codeql/swift/dataflow/Ssa.qll @@ -39,6 +39,11 @@ module Ssa { read2 = bb2.getNode(i2) ) } + + cached + predicate lastRefRedef(BasicBlock bb, int i, Definition next) { + SsaImplCommon::lastRefRedef(this, bb, i, next) + } } cached diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll index e530df2fc20..efe445dcb31 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll @@ -5,7 +5,6 @@ private import codeql.swift.controlflow.ControlFlowGraph private import codeql.swift.controlflow.CfgNodes private import codeql.swift.dataflow.Ssa private import codeql.swift.controlflow.BasicBlocks -private import codeql.swift.dataflow.internal.SsaImplCommon as SsaImpl private import codeql.swift.dataflow.FlowSummary as FlowSummary private import codeql.swift.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl @@ -51,7 +50,7 @@ private class SsaDefinitionNodeImpl extends SsaDefinitionNode, NodeImpl { } private predicate localFlowSsaInput(Node nodeFrom, Ssa::Definition def, Ssa::Definition next) { - exists(BasicBlock bb, int i | SsaImpl::lastRefRedef(def, bb, i, next) | + exists(BasicBlock bb, int i | def.lastRefRedef(bb, i, next) | def.definesAt(_, bb, i) and def = nodeFrom.asDefinition() ) From c59e6586f7016245c2870737454ea9ce0c46e8bd Mon Sep 17 00:00:00 2001 From: intrigus-lgtm <60750685+intrigus-lgtm@users.noreply.github.com> Date: Wed, 3 Aug 2022 14:19:53 +0200 Subject: [PATCH 423/505] Add additional reference to CERT C coding standard --- .../Security/CWE/CWE-273/PrivilegeDroppingOutoforder.qhelp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-273/PrivilegeDroppingOutoforder.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-273/PrivilegeDroppingOutoforder.qhelp index ca8d8dfaf22..1daebb58b3c 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-273/PrivilegeDroppingOutoforder.qhelp +++ b/cpp/ql/src/experimental/Security/CWE/CWE-273/PrivilegeDroppingOutoforder.qhelp @@ -27,6 +27,9 @@ groups, and finally set the target user.

    +
  • CERT C Coding Standard: +POS36-C. Observe correct revocation order while relinquishing privileges. +
  • CERT C Coding Standard: POS37-C. Ensure that privilege relinquishment is successful.
  • From c635895644fe58b5dc93e899ccd3356647ac8af9 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 3 Aug 2022 10:06:08 +0100 Subject: [PATCH 424/505] Swift: Documentation. --- .../Security/CWE-095/UnsafeWebViewFetch.qhelp | 14 +++++++------- .../queries/Security/CWE-095/UnsafeWebViewFetch.ql | 2 +- .../Security/CWE-095/UnsafeWebViewFetchBad.swift | 6 +++++- .../Security/CWE-095/UnsafeWebViewFetchGood.swift | 6 +++++- .../Security/CWE-095/UnsafeWebViewFetch.expected | 7 +++++++ .../Security/CWE-095/UnsafeWebViewFetch.swift | 11 +++++++++++ 6 files changed, 36 insertions(+), 10 deletions(-) diff --git a/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetch.qhelp b/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetch.qhelp index a58e46b5aff..1d61dbe9e92 100644 --- a/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetch.qhelp +++ b/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetch.qhelp @@ -3,29 +3,29 @@ "qhelp.dtd"> -

    TODO

    +

    Fetching data in a WebView without restricting the base URL may allow an attacker to access sensitive local data, for example using file://. Data can then be extracted from the software using the URL of a machine under the attackers control. More generally, an attacker may use a URL under their control as part of a cross-site scripting attack.

    -

    TODO

    +

    When loading HTML into a web view, always set the baseURL to an appropriate URL that you control, or to about:blank. Do not use nil, as this does not restrict URLs that can be resolved. Also do not use a baseURL that could itself be controlled by an attacker.

    -

    TODO

    +

    In the following example, a call to UIWebView.loadHTMLString has the baseURL set to nil, which does not restrict URLs that can be resolved from within the web page.

    - + -

    TODO

    +

    To fix the problem, we set the baseURL to about:blank. This ensures that an attacker cannot resolve URLs that point to the local file system, or to web servers under their control.

    - +
  • - TODO + iOS Bug Hunting - Web View XSS
  • diff --git a/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetch.ql b/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetch.ql index c8677f704a1..5ddd9bc7d88 100644 --- a/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetch.ql +++ b/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetch.ql @@ -1,6 +1,6 @@ /** * @name Unsafe WebView fetch - * @description TODO + * @description Fetching data in a WebView without restricting the base URL may allow an attacker to access sensitive local data, or enable cross-site scripting attack. * @kind path-problem * @problem.severity warning * @security-severity 6.1 diff --git a/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetchBad.swift b/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetchBad.swift index 6921ceac35d..f85adb1b267 100644 --- a/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetchBad.swift +++ b/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetchBad.swift @@ -1,2 +1,6 @@ -TODO +let webview = UIWebView() + +... + +webview.loadHTMLString(htmlData, baseURL: nil) // BAD diff --git a/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetchGood.swift b/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetchGood.swift index 6921ceac35d..5d89e5be672 100644 --- a/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetchGood.swift +++ b/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetchGood.swift @@ -1,2 +1,6 @@ -TODO +let webview = UIWebView() + +... + +webview.loadHTMLString(htmlData, baseURL: URL(string: "about:blank")) // GOOD diff --git a/swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.expected b/swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.expected index 9d3ef5ef4f0..d387127ac4e 100644 --- a/swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.expected +++ b/swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.expected @@ -3,6 +3,7 @@ edges | UnsafeWebViewFetch.swift:94:10:94:37 | try ... : | UnsafeWebViewFetch.swift:120:25:120:39 | call to getRemoteData() | | UnsafeWebViewFetch.swift:94:10:94:37 | try ... : | UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | | UnsafeWebViewFetch.swift:94:10:94:37 | try ... : | UnsafeWebViewFetch.swift:167:25:167:39 | call to getRemoteData() | +| UnsafeWebViewFetch.swift:94:10:94:37 | try ... : | UnsafeWebViewFetch.swift:206:17:206:31 | call to getRemoteData() : | | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:94:10:94:37 | try ... : | | UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:121:25:121:25 | remoteString | | UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:124:25:124:51 | ... call to +(_:_:) ... | @@ -28,6 +29,8 @@ edges | UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:188:48:188:58 | ...! | | UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:200:90:200:99 | ...! | | UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:201:91:201:100 | ...! | +| UnsafeWebViewFetch.swift:206:17:206:31 | call to getRemoteData() : | UnsafeWebViewFetch.swift:210:25:210:25 | htmlData | +| UnsafeWebViewFetch.swift:206:17:206:31 | call to getRemoteData() : | UnsafeWebViewFetch.swift:211:25:211:25 | htmlData | nodes | UnsafeWebViewFetch.swift:94:10:94:37 | try ... : | semmle.label | try ... : | | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | semmle.label | call to ... : | @@ -59,6 +62,9 @@ nodes | UnsafeWebViewFetch.swift:188:48:188:58 | ...! | semmle.label | ...! | | UnsafeWebViewFetch.swift:200:90:200:99 | ...! | semmle.label | ...! | | UnsafeWebViewFetch.swift:201:91:201:100 | ...! | semmle.label | ...! | +| UnsafeWebViewFetch.swift:206:17:206:31 | call to getRemoteData() : | semmle.label | call to getRemoteData() : | +| UnsafeWebViewFetch.swift:210:25:210:25 | htmlData | semmle.label | htmlData | +| UnsafeWebViewFetch.swift:211:25:211:25 | htmlData | semmle.label | htmlData | subpaths #select | UnsafeWebViewFetch.swift:120:25:120:39 | call to getRemoteData() | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:120:25:120:39 | call to getRemoteData() | Tainted data is used in a WebView fetch without restricting the base URL. | @@ -71,3 +77,4 @@ subpaths | UnsafeWebViewFetch.swift:171:25:171:51 | ... call to +(_:_:) ... | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:171:25:171:51 | ... call to +(_:_:) ... | Tainted data is used in a WebView fetch without restricting the base URL. | | UnsafeWebViewFetch.swift:186:25:186:25 | remoteString | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:186:25:186:25 | remoteString | Tainted data is used in a WebView fetch with a tainted base URL. | | UnsafeWebViewFetch.swift:188:25:188:25 | remoteString | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:188:25:188:25 | remoteString | Tainted data is used in a WebView fetch with a tainted base URL. | +| UnsafeWebViewFetch.swift:210:25:210:25 | htmlData | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:210:25:210:25 | htmlData | Tainted data is used in a WebView fetch without restricting the base URL. | diff --git a/swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.swift b/swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.swift index 3fc5aca878f..9dcef0262ec 100644 --- a/swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.swift +++ b/swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.swift @@ -201,6 +201,17 @@ func testWKWebView() { webview.load(remoteData, mimeType: "text/html", characterEncodingName: "utf-8", baseURL: remoteURL!) // BAD [NOT DETECTED] } +func testQHelpExamples() { + let webview = UIWebView() + let htmlData = getRemoteData() + + // ... + + webview.loadHTMLString(htmlData, baseURL: nil) // BAD + webview.loadHTMLString(htmlData, baseURL: URL(string: "about:blank")) // GOOD +} + testSimpleFlows() testUIWebView() testWKWebView() +testQHelpExamples() From 81bd61288c1198fac599e3e39aadedcbf3a42538 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 3 Aug 2022 14:39:35 +0100 Subject: [PATCH 425/505] Swift: I think CWE-079 is the more accurate CWE for this query. --- .../Security/{CWE-095 => CWE-079}/UnsafeWebViewFetch.qhelp | 0 .../queries/Security/{CWE-095 => CWE-079}/UnsafeWebViewFetch.ql | 0 .../Security/{CWE-095 => CWE-079}/UnsafeWebViewFetchBad.swift | 0 .../Security/{CWE-095 => CWE-079}/UnsafeWebViewFetchGood.swift | 0 .../Security/{CWE-095 => CWE-079}/UnsafeWebViewFetch.expected | 0 .../Security/{CWE-095 => CWE-079}/UnsafeWebViewFetch.qlref | 0 .../Security/{CWE-095 => CWE-079}/UnsafeWebViewFetch.swift | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename swift/ql/src/queries/Security/{CWE-095 => CWE-079}/UnsafeWebViewFetch.qhelp (100%) rename swift/ql/src/queries/Security/{CWE-095 => CWE-079}/UnsafeWebViewFetch.ql (100%) rename swift/ql/src/queries/Security/{CWE-095 => CWE-079}/UnsafeWebViewFetchBad.swift (100%) rename swift/ql/src/queries/Security/{CWE-095 => CWE-079}/UnsafeWebViewFetchGood.swift (100%) rename swift/ql/test/query-tests/Security/{CWE-095 => CWE-079}/UnsafeWebViewFetch.expected (100%) rename swift/ql/test/query-tests/Security/{CWE-095 => CWE-079}/UnsafeWebViewFetch.qlref (100%) rename swift/ql/test/query-tests/Security/{CWE-095 => CWE-079}/UnsafeWebViewFetch.swift (100%) diff --git a/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetch.qhelp b/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.qhelp similarity index 100% rename from swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetch.qhelp rename to swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.qhelp diff --git a/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetch.ql b/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql similarity index 100% rename from swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetch.ql rename to swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql diff --git a/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetchBad.swift b/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetchBad.swift similarity index 100% rename from swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetchBad.swift rename to swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetchBad.swift diff --git a/swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetchGood.swift b/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetchGood.swift similarity index 100% rename from swift/ql/src/queries/Security/CWE-095/UnsafeWebViewFetchGood.swift rename to swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetchGood.swift diff --git a/swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.expected b/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.expected similarity index 100% rename from swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.expected rename to swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.expected diff --git a/swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.qlref b/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.qlref similarity index 100% rename from swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.qlref rename to swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.qlref diff --git a/swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.swift b/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.swift similarity index 100% rename from swift/ql/test/query-tests/Security/CWE-095/UnsafeWebViewFetch.swift rename to swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.swift From 39f135284720b9f77b23e012235eaf79e96de20e Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 3 Aug 2022 14:40:13 +0100 Subject: [PATCH 426/505] Swift: Complete the rename. --- swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql | 2 +- .../test/query-tests/Security/CWE-079/UnsafeWebViewFetch.qlref | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql b/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql index 5ddd9bc7d88..87b47dc6006 100644 --- a/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql +++ b/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql @@ -7,8 +7,8 @@ * @precision high * @id swift/unsafe-webview-fetch * @tags security - * external/cwe/cwe-095 * external/cwe/cwe-079 + * external/cwe/cwe-095 * external/cwe/cwe-749 */ diff --git a/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.qlref b/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.qlref index ddd18844328..57e842e89ff 100644 --- a/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.qlref +++ b/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.qlref @@ -1 +1 @@ -queries/Security/CWE-095/UnsafeWebviewFetch.ql \ No newline at end of file +queries/Security/CWE-079/UnsafeWebviewFetch.ql \ No newline at end of file From 9d499863456aee7d5cc8f66abacff3df30558be4 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 3 Aug 2022 17:18:57 +0100 Subject: [PATCH 427/505] Swift: Make QL-for-QL happy. --- .../Security/CWE-079/UnsafeWebViewFetch.ql | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql b/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql index 87b47dc6006..bc868d01929 100644 --- a/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql +++ b/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql @@ -23,8 +23,8 @@ import codeql.swift.frameworks.StandardLibrary.String * A taint source that is `String(contentsOf:)`. * TODO: this shouldn't be needed when `StringSource` in `String.qll` is working. */ -class StringContentsOfURLSource extends RemoteFlowSource { - StringContentsOfURLSource() { +class StringContentsOfUrlSource extends RemoteFlowSource { + StringContentsOfUrlSource() { exists(CallExpr call, AbstractFunctionDecl f | call.getFunction().(ApplyExpr).getStaticTarget() = f and f.getName() = "init(contentsOf:)" and @@ -41,12 +41,12 @@ class StringContentsOfURLSource extends RemoteFlowSource { * to `UIWebView.loadHTMLString`. */ class Sink extends DataFlow::Node { - Expr baseURL; + Expr baseUrl; Sink() { exists( AbstractFunctionDecl funcDecl, CallExpr call, string funcName, string paramName, int arg, - int baseURLarg + int baseUrlArg | // arguments to method calls... exists(string className, ClassDecl c | @@ -75,19 +75,19 @@ class Sink extends DataFlow::Node { funcDecl.getParam(pragma[only_bind_into](arg)).getName() = paramName and call.getArgument(pragma[only_bind_into](arg)).getExpr() = this.asExpr() and // match up `baseURLArg` - funcDecl.getParam(pragma[only_bind_into](baseURLarg)).getName() = "baseURL" and - call.getArgument(pragma[only_bind_into](baseURLarg)).getExpr() = baseURL + funcDecl.getParam(pragma[only_bind_into](baseUrlArg)).getName() = "baseURL" and + call.getArgument(pragma[only_bind_into](baseUrlArg)).getExpr() = baseUrl ) } /** * Gets the `baseURL` argument associated with this sink. */ - Expr getBaseURL() { result = baseURL } + Expr getBaseUrl() { result = baseUrl } } /** - * Taint configuration from taint sources to sinks (and `baseURL` arguments) + * A taint configuration from taint sources to sinks (and `baseURL` arguments) * for this query. */ class UnsafeWebViewFetchConfig extends TaintTracking::Configuration { @@ -133,11 +133,11 @@ where sink = sinkNode.getNode() and ( // base URL is nil - sink.getBaseURL() instanceof NilLiteralExpr and + sink.getBaseUrl() instanceof NilLiteralExpr and message = "Tainted data is used in a WebView fetch without restricting the base URL." or // base URL is tainted - config.hasFlow(_, any(DataFlow::Node n | n.asExpr() = sink.getBaseURL())) and + config.hasFlow(_, any(DataFlow::Node n | n.asExpr() = sink.getBaseUrl())) and message = "Tainted data is used in a WebView fetch with a tainted base URL." ) select sinkNode, sourceNode, sinkNode, message From e4dab173185f59752a71c2f4fae40060e0ee6aad Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 3 Aug 2022 18:14:14 +0100 Subject: [PATCH 428/505] Apply suggestions from code review Co-authored-by: Mathias Vorreiter Pedersen --- swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql b/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql index bc868d01929..b535d346c7f 100644 --- a/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql +++ b/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql @@ -119,7 +119,7 @@ class UnsafeWebViewFetchConfig extends TaintTracking::Configuration { c.getAMember() = f and f.getName() = ["init(string:)", "init(string:relativeTo:)"] and call.getFunction().(ApplyExpr).getStaticTarget() = f and - node1.asExpr() = call.getArgument(_).getExpr() and + node1.asExpr() = call.getAnArgument().getExpr() and node2.asExpr() = call ) } @@ -140,4 +140,4 @@ where config.hasFlow(_, any(DataFlow::Node n | n.asExpr() = sink.getBaseUrl())) and message = "Tainted data is used in a WebView fetch with a tainted base URL." ) -select sinkNode, sourceNode, sinkNode, message +select sink, sourceNode, sinkNode, message From 873c62ef78b895e9580a6bcd8cf76dbfc7cdf5e3 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 3 Aug 2022 18:16:01 +0100 Subject: [PATCH 429/505] Swift: Apply another code review suggestion. --- swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql b/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql index b535d346c7f..b5e6adafd4c 100644 --- a/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql +++ b/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql @@ -137,7 +137,7 @@ where message = "Tainted data is used in a WebView fetch without restricting the base URL." or // base URL is tainted - config.hasFlow(_, any(DataFlow::Node n | n.asExpr() = sink.getBaseUrl())) and + config.hasFlowToExpr(sink.getBaseUrl()) and message = "Tainted data is used in a WebView fetch with a tainted base URL." ) select sink, sourceNode, sinkNode, message From 997068a9cb1f287bc3d1553c6f73ae61ec7051bb Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 3 Aug 2022 18:16:31 +0100 Subject: [PATCH 430/505] Swift: Fix a suggestion merge conflict. --- swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql b/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql index b5e6adafd4c..2f853db2975 100644 --- a/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql +++ b/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql @@ -97,7 +97,7 @@ class UnsafeWebViewFetchConfig extends TaintTracking::Configuration { override predicate isSink(DataFlow::Node node) { node instanceof Sink or - node.asExpr() = any(Sink s).getBaseURL() + node.asExpr() = any(Sink s).getBaseUrl() } override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { From fdbe16945feef3df0c5c7778eb053529801ecdc2 Mon Sep 17 00:00:00 2001 From: Harry Maclean Date: Thu, 4 Aug 2022 17:19:05 +1200 Subject: [PATCH 431/505] Ruby: Add change note --- ruby/ql/src/change-notes/2022-08-04-mime-type.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 ruby/ql/src/change-notes/2022-08-04-mime-type.md diff --git a/ruby/ql/src/change-notes/2022-08-04-mime-type.md b/ruby/ql/src/change-notes/2022-08-04-mime-type.md new file mode 100644 index 00000000000..033e8ed626c --- /dev/null +++ b/ruby/ql/src/change-notes/2022-08-04-mime-type.md @@ -0,0 +1,5 @@ +--- +category: minorAnalysis +--- +* Arguments to `Mime::Type#match?` and `Mime::Type#=~` are now recognised as + regular expression sources. From def1b3c3b3fef53f7e5a5f0caaa9fe270d922043 Mon Sep 17 00:00:00 2001 From: Harry Maclean Date: Thu, 4 Aug 2022 17:21:29 +1200 Subject: [PATCH 432/505] Ruby: QLDoc fix --- ruby/ql/lib/codeql/ruby/Regexp.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruby/ql/lib/codeql/ruby/Regexp.qll b/ruby/ql/lib/codeql/ruby/Regexp.qll index 23237c28b0b..a0454d0f5fd 100644 --- a/ruby/ql/lib/codeql/ruby/Regexp.qll +++ b/ruby/ql/lib/codeql/ruby/Regexp.qll @@ -103,7 +103,7 @@ module RegExpInterpretation { } /** - * Nodes interpreted as regular expressions via various standard library methods. + * A node interpreted as a regular expression. */ class StdLibRegExpInterpretation extends RegExpInterpretation::Range { StdLibRegExpInterpretation() { From 7ed81db32d24b630c8b0225ca82ac49d4a1d2139 Mon Sep 17 00:00:00 2001 From: Harry Maclean Date: Tue, 19 Jul 2022 12:29:27 +1200 Subject: [PATCH 433/505] Ruby: Move ActiveRecord tests to new directory --- .../frameworks/ActionController.expected | 76 +++++++++---------- .../{ => active_record}/ActiveRecord.expected | 0 .../{ => active_record}/ActiveRecord.ql | 0 .../{ => active_record}/ActiveRecord.rb | 0 4 files changed, 38 insertions(+), 38 deletions(-) rename ruby/ql/test/library-tests/frameworks/{ => active_record}/ActiveRecord.expected (100%) rename ruby/ql/test/library-tests/frameworks/{ => active_record}/ActiveRecord.ql (100%) rename ruby/ql/test/library-tests/frameworks/{ => active_record}/ActiveRecord.rb (100%) diff --git a/ruby/ql/test/library-tests/frameworks/ActionController.expected b/ruby/ql/test/library-tests/frameworks/ActionController.expected index 52ab15995c7..b935d3fde23 100644 --- a/ruby/ql/test/library-tests/frameworks/ActionController.expected +++ b/ruby/ql/test/library-tests/frameworks/ActionController.expected @@ -1,20 +1,20 @@ actionControllerControllerClasses -| ActiveRecord.rb:23:1:39:3 | FooController | -| ActiveRecord.rb:41:1:64:3 | BarController | -| ActiveRecord.rb:66:1:70:3 | BazController | -| ActiveRecord.rb:72:1:80:3 | AnnotatedController | +| active_record/ActiveRecord.rb:23:1:39:3 | FooController | +| active_record/ActiveRecord.rb:41:1:64:3 | BarController | +| active_record/ActiveRecord.rb:66:1:70:3 | BazController | +| active_record/ActiveRecord.rb:72:1:80:3 | AnnotatedController | | app/controllers/comments_controller.rb:1:1:7:3 | CommentsController | | app/controllers/foo/bars_controller.rb:3:1:39:3 | BarsController | | app/controllers/photos_controller.rb:1:1:4:3 | PhotosController | | app/controllers/posts_controller.rb:1:1:10:3 | PostsController | | app/controllers/users/notifications_controller.rb:2:3:5:5 | NotificationsController | actionControllerActionMethods -| ActiveRecord.rb:27:3:38:5 | some_request_handler | -| ActiveRecord.rb:42:3:47:5 | some_other_request_handler | -| ActiveRecord.rb:49:3:63:5 | safe_paths | -| ActiveRecord.rb:67:3:69:5 | yet_another_handler | -| ActiveRecord.rb:73:3:75:5 | index | -| ActiveRecord.rb:77:3:79:5 | unsafe_action | +| active_record/ActiveRecord.rb:27:3:38:5 | some_request_handler | +| active_record/ActiveRecord.rb:42:3:47:5 | some_other_request_handler | +| active_record/ActiveRecord.rb:49:3:63:5 | safe_paths | +| active_record/ActiveRecord.rb:67:3:69:5 | yet_another_handler | +| active_record/ActiveRecord.rb:73:3:75:5 | index | +| active_record/ActiveRecord.rb:77:3:79:5 | unsafe_action | | app/controllers/comments_controller.rb:2:3:3:5 | index | | app/controllers/comments_controller.rb:5:3:6:5 | show | | app/controllers/foo/bars_controller.rb:5:3:7:5 | index | @@ -28,40 +28,40 @@ actionControllerActionMethods | app/controllers/posts_controller.rb:8:3:9:5 | upvote | | app/controllers/users/notifications_controller.rb:3:5:4:7 | mark_as_read | paramsCalls -| ActiveRecord.rb:28:30:28:35 | call to params | -| ActiveRecord.rb:29:29:29:34 | call to params | -| ActiveRecord.rb:30:31:30:36 | call to params | -| ActiveRecord.rb:32:21:32:26 | call to params | -| ActiveRecord.rb:34:34:34:39 | call to params | -| ActiveRecord.rb:35:23:35:28 | call to params | -| ActiveRecord.rb:35:38:35:43 | call to params | -| ActiveRecord.rb:43:10:43:15 | call to params | -| ActiveRecord.rb:50:11:50:16 | call to params | -| ActiveRecord.rb:54:12:54:17 | call to params | -| ActiveRecord.rb:59:12:59:17 | call to params | -| ActiveRecord.rb:62:15:62:20 | call to params | -| ActiveRecord.rb:68:21:68:26 | call to params | -| ActiveRecord.rb:78:59:78:64 | call to params | +| active_record/ActiveRecord.rb:28:30:28:35 | call to params | +| active_record/ActiveRecord.rb:29:29:29:34 | call to params | +| active_record/ActiveRecord.rb:30:31:30:36 | call to params | +| active_record/ActiveRecord.rb:32:21:32:26 | call to params | +| active_record/ActiveRecord.rb:34:34:34:39 | call to params | +| active_record/ActiveRecord.rb:35:23:35:28 | call to params | +| active_record/ActiveRecord.rb:35:38:35:43 | call to params | +| active_record/ActiveRecord.rb:43:10:43:15 | call to params | +| active_record/ActiveRecord.rb:50:11:50:16 | call to params | +| active_record/ActiveRecord.rb:54:12:54:17 | call to params | +| active_record/ActiveRecord.rb:59:12:59:17 | call to params | +| active_record/ActiveRecord.rb:62:15:62:20 | call to params | +| active_record/ActiveRecord.rb:68:21:68:26 | call to params | +| active_record/ActiveRecord.rb:78:59:78:64 | call to params | | app/controllers/foo/bars_controller.rb:13:21:13:26 | call to params | | app/controllers/foo/bars_controller.rb:14:10:14:15 | call to params | | app/controllers/foo/bars_controller.rb:21:21:21:26 | call to params | | app/controllers/foo/bars_controller.rb:22:10:22:15 | call to params | | app/views/foo/bars/show.html.erb:5:9:5:14 | call to params | paramsSources -| ActiveRecord.rb:28:30:28:35 | call to params | -| ActiveRecord.rb:29:29:29:34 | call to params | -| ActiveRecord.rb:30:31:30:36 | call to params | -| ActiveRecord.rb:32:21:32:26 | call to params | -| ActiveRecord.rb:34:34:34:39 | call to params | -| ActiveRecord.rb:35:23:35:28 | call to params | -| ActiveRecord.rb:35:38:35:43 | call to params | -| ActiveRecord.rb:43:10:43:15 | call to params | -| ActiveRecord.rb:50:11:50:16 | call to params | -| ActiveRecord.rb:54:12:54:17 | call to params | -| ActiveRecord.rb:59:12:59:17 | call to params | -| ActiveRecord.rb:62:15:62:20 | call to params | -| ActiveRecord.rb:68:21:68:26 | call to params | -| ActiveRecord.rb:78:59:78:64 | call to params | +| active_record/ActiveRecord.rb:28:30:28:35 | call to params | +| active_record/ActiveRecord.rb:29:29:29:34 | call to params | +| active_record/ActiveRecord.rb:30:31:30:36 | call to params | +| active_record/ActiveRecord.rb:32:21:32:26 | call to params | +| active_record/ActiveRecord.rb:34:34:34:39 | call to params | +| active_record/ActiveRecord.rb:35:23:35:28 | call to params | +| active_record/ActiveRecord.rb:35:38:35:43 | call to params | +| active_record/ActiveRecord.rb:43:10:43:15 | call to params | +| active_record/ActiveRecord.rb:50:11:50:16 | call to params | +| active_record/ActiveRecord.rb:54:12:54:17 | call to params | +| active_record/ActiveRecord.rb:59:12:59:17 | call to params | +| active_record/ActiveRecord.rb:62:15:62:20 | call to params | +| active_record/ActiveRecord.rb:68:21:68:26 | call to params | +| active_record/ActiveRecord.rb:78:59:78:64 | call to params | | app/controllers/foo/bars_controller.rb:13:21:13:26 | call to params | | app/controllers/foo/bars_controller.rb:14:10:14:15 | call to params | | app/controllers/foo/bars_controller.rb:21:21:21:26 | call to params | diff --git a/ruby/ql/test/library-tests/frameworks/ActiveRecord.expected b/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.expected similarity index 100% rename from ruby/ql/test/library-tests/frameworks/ActiveRecord.expected rename to ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.expected diff --git a/ruby/ql/test/library-tests/frameworks/ActiveRecord.ql b/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.ql similarity index 100% rename from ruby/ql/test/library-tests/frameworks/ActiveRecord.ql rename to ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.ql diff --git a/ruby/ql/test/library-tests/frameworks/ActiveRecord.rb b/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.rb similarity index 100% rename from ruby/ql/test/library-tests/frameworks/ActiveRecord.rb rename to ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.rb From d4f7f2b75e39627df5a6294dea09b75f43f0352d Mon Sep 17 00:00:00 2001 From: Harry Maclean Date: Tue, 19 Jul 2022 12:40:23 +1200 Subject: [PATCH 434/505] Ruby: Add test for AR PersistentWriteAccesses --- .../frameworks/active_record/ActiveRecord.ql | 6 ++++++ .../frameworks/active_record/ActiveRecord.rb | 16 ++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.ql b/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.ql index 6d19f9bb9ec..731679e437b 100644 --- a/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.ql +++ b/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.ql @@ -1,5 +1,7 @@ import codeql.ruby.controlflow.CfgNodes import codeql.ruby.frameworks.ActiveRecord +import codeql.ruby.Concepts +import codeql.ruby.DataFlow query predicate activeRecordModelClasses(ActiveRecordModelClass cls) { any() } @@ -18,3 +20,7 @@ query predicate activeRecordModelInstantiations( ) { i.getClass() = cls } + +query predicate persistentWriteAccesses(PersistentWriteAccess w, DataFlow::Node value) { + w.getValue() = value +} diff --git a/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.rb b/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.rb index d25cbf901c3..561c4eff732 100644 --- a/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.rb +++ b/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.rb @@ -67,6 +67,22 @@ class BazController < BarController def yet_another_handler Admin.delete_by(params[:admin_condition]) end + + def create1 + Admin.create(params) + end + + def create2 + Admin.create(name: params[:name]) + end + + def update1 + Admin.update(params) + end + + def update2 + Admin.update(name: params[:name]) + end end class AnnotatedController < ActionController::Base From 21b4918904386d8cfc0619cacce74eac4370a99a Mon Sep 17 00:00:00 2001 From: Harry Maclean Date: Tue, 19 Jul 2022 13:06:57 +1200 Subject: [PATCH 435/505] Ruby: Add getPositionalArgument This gets positional arguments from a call. These are arguments which are not keyword arguments. --- ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll | 8 ++++++++ .../lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll b/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll index 29668b82e70..143030c0414 100644 --- a/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll +++ b/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll @@ -357,6 +357,14 @@ module ExprNodes { ) } + /** + * Gets the `nth` positional argument of this call. + * Unlike `getArgument`, this excludes keyword arguments. + */ + final ExprCfgNode getPositionalArgument(int n) { + result = this.getArgument(n) and not result instanceof PairCfgNode + } + /** Gets the number of arguments of this call. */ final int getNumberOfArguments() { result = e.getNumberOfArguments() } diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll index 730445d99ac..21476fc4562 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll @@ -71,6 +71,14 @@ class CallNode extends LocalSourceNode, ExprNode { /** Gets the data-flow node corresponding to the named argument of the call corresponding to this data-flow node */ ExprNode getKeywordArgument(string name) { result.getExprNode() = node.getKeywordArgument(name) } + /** + * Gets the `nth` positional argument of this call. + * Unlike `getArgument`, this excludes keyword arguments. + */ + final ExprNode getPositionalArgument(int n) { + result.getExprNode() = node.getPositionalArgument(n) + } + /** Gets the name of the the method called by the method call (if any) corresponding to this data-flow node */ string getMethodName() { result = node.getExpr().(MethodCall).getMethodName() } From 83393dc19556d979e7b280c235c547dcaa0c145c Mon Sep 17 00:00:00 2001 From: Harry Maclean Date: Tue, 19 Jul 2022 13:11:12 +1200 Subject: [PATCH 436/505] Ruby: Recognise more AR write accesses This change means we recognise calls like ```rb User.create(params) User.update(id, params) ``` as instances of `PersistentWriteAccess`. --- .../lib/codeql/ruby/controlflow/CfgNodes.qll | 2 +- .../ruby/dataflow/internal/DataFlowPublic.qll | 2 +- .../codeql/ruby/frameworks/ActiveRecord.qll | 33 +++++++++------- .../concepts/PersistentWriteAccess.expected | 5 +++ .../app/controllers/users_controller.rb | 10 +++++ .../frameworks/ActionController.expected | 38 ++++++++++++++++--- .../active_record/ActiveRecord.expected | 24 ++++++++++-- .../frameworks/active_record/ActiveRecord.rb | 14 +++++-- 8 files changed, 100 insertions(+), 28 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll b/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll index 143030c0414..5cd5573cd0b 100644 --- a/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll +++ b/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll @@ -358,7 +358,7 @@ module ExprNodes { } /** - * Gets the `nth` positional argument of this call. + * Gets the `n`th positional argument of this call. * Unlike `getArgument`, this excludes keyword arguments. */ final ExprCfgNode getPositionalArgument(int n) { diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll index 21476fc4562..c946a79b826 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll @@ -72,7 +72,7 @@ class CallNode extends LocalSourceNode, ExprNode { ExprNode getKeywordArgument(string name) { result.getExprNode() = node.getKeywordArgument(name) } /** - * Gets the `nth` positional argument of this call. + * Gets the `n`th positional argument of this call. * Unlike `getArgument`, this excludes keyword arguments. */ final ExprNode getPositionalArgument(int n) { diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll index 142d1455ce4..a983005e766 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll @@ -364,15 +364,6 @@ private module Persistence { ) } - /** - * Holds if `call` has a keyword argument with value `value`. - */ - private predicate keywordArgumentWithValue(DataFlow::CallNode call, DataFlow::ExprNode value) { - exists(ExprNodes::PairCfgNode pair | pair = call.getArgument(_).asExpr() | - value.asExpr() = pair.getValue() - ) - } - /** A call to e.g. `User.create(name: "foo")` */ private class CreateLikeCall extends DataFlow::CallNode, PersistentWriteAccess::Range { CreateLikeCall() { @@ -386,8 +377,12 @@ private module Persistence { override DataFlow::Node getValue() { // attrs as hash elements in arg0 - hashArgumentWithValue(this, 0, result) or - keywordArgumentWithValue(this, result) + hashArgumentWithValue(this, 0, result) + or + result = this.getKeywordArgument(_) + or + result = this.getPositionalArgument(0) and + not result.asExpr() instanceof ExprNodes::HashLiteralCfgNode } } @@ -399,11 +394,19 @@ private module Persistence { } override DataFlow::Node getValue() { - keywordArgumentWithValue(this, result) + // User.update(1, name: "foo") + result = this.getKeywordArgument(_) + or + // User.update(1, params) + exists(int n | n > 0 | + result = this.getPositionalArgument(n) and + not result.asExpr() instanceof ExprNodes::ArrayLiteralCfgNode + ) or // Case where 2 array args are passed - the first an array of IDs, and the // second an array of hashes - each hash corresponding to an ID in the // first array. + // User.update([1,2,3], [{name: "foo"}, {name: "bar"}]) exists(ExprNodes::ArrayLiteralCfgNode hashesArray | this.getArgument(0).asExpr() instanceof ExprNodes::ArrayLiteralCfgNode and hashesArray = this.getArgument(1).asExpr() @@ -472,8 +475,12 @@ private module Persistence { // attrs as hash elements in arg0 hashArgumentWithValue(this, 0, result) or + // attrs as variable in arg0 + result = this.getPositionalArgument(0) and + not result.asExpr() instanceof ExprNodes::HashLiteralCfgNode + or // keyword arg - keywordArgumentWithValue(this, result) + result = this.getKeywordArgument(_) } } diff --git a/ruby/ql/test/library-tests/concepts/PersistentWriteAccess.expected b/ruby/ql/test/library-tests/concepts/PersistentWriteAccess.expected index f71e5f89116..64d01e3e93d 100644 --- a/ruby/ql/test/library-tests/concepts/PersistentWriteAccess.expected +++ b/ruby/ql/test/library-tests/concepts/PersistentWriteAccess.expected @@ -15,6 +15,11 @@ | app/controllers/users_controller.rb:23:7:23:42 | call to update_attribute | app/controllers/users_controller.rb:23:37:23:41 | "U13" | | app/controllers/users_controller.rb:26:19:26:23 | ... = ... | app/controllers/users_controller.rb:26:19:26:23 | "U14" | | app/controllers/users_controller.rb:31:7:31:32 | call to touch_all | app/controllers/users_controller.rb:31:28:31:31 | call to time | +| app/controllers/users_controller.rb:35:7:35:27 | call to update | app/controllers/users_controller.rb:35:22:35:26 | attrs | +| app/controllers/users_controller.rb:36:7:36:28 | call to update! | app/controllers/users_controller.rb:36:23:36:27 | attrs | +| app/controllers/users_controller.rb:39:7:39:24 | call to create | app/controllers/users_controller.rb:39:19:39:23 | attrs | +| app/controllers/users_controller.rb:40:7:40:25 | call to create! | app/controllers/users_controller.rb:40:20:40:24 | attrs | +| app/controllers/users_controller.rb:41:7:41:24 | call to insert | app/controllers/users_controller.rb:41:19:41:23 | attrs | | app/models/user.rb:4:5:4:28 | call to update | app/models/user.rb:4:23:4:27 | "U15" | | app/models/user.rb:5:5:5:23 | call to update | app/models/user.rb:5:18:5:22 | "U16" | | app/models/user.rb:6:5:6:56 | call to update_attributes | app/models/user.rb:6:35:6:39 | "U17" | diff --git a/ruby/ql/test/library-tests/concepts/app/controllers/users_controller.rb b/ruby/ql/test/library-tests/concepts/app/controllers/users_controller.rb index 83ee6088527..062c643e0ae 100644 --- a/ruby/ql/test/library-tests/concepts/app/controllers/users_controller.rb +++ b/ruby/ql/test/library-tests/concepts/app/controllers/users_controller.rb @@ -29,6 +29,16 @@ module Users # TouchAllCall User.touch_all User.touch_all(time: time) + + # UpdateLikeClassMethodCall + attrs = {name: "U15"} + User.update(8, attrs) + User.update!(8, attrs) + + # CreateLikeClassMethodCall + User.create(attrs) + User.create!(attrs) + User.insert(attrs) end def get_uid diff --git a/ruby/ql/test/library-tests/frameworks/ActionController.expected b/ruby/ql/test/library-tests/frameworks/ActionController.expected index b935d3fde23..d6de776bbff 100644 --- a/ruby/ql/test/library-tests/frameworks/ActionController.expected +++ b/ruby/ql/test/library-tests/frameworks/ActionController.expected @@ -1,8 +1,8 @@ actionControllerControllerClasses | active_record/ActiveRecord.rb:23:1:39:3 | FooController | | active_record/ActiveRecord.rb:41:1:64:3 | BarController | -| active_record/ActiveRecord.rb:66:1:70:3 | BazController | -| active_record/ActiveRecord.rb:72:1:80:3 | AnnotatedController | +| active_record/ActiveRecord.rb:66:1:94:3 | BazController | +| active_record/ActiveRecord.rb:96:1:104:3 | AnnotatedController | | app/controllers/comments_controller.rb:1:1:7:3 | CommentsController | | app/controllers/foo/bars_controller.rb:3:1:39:3 | BarsController | | app/controllers/photos_controller.rb:1:1:4:3 | PhotosController | @@ -13,8 +13,14 @@ actionControllerActionMethods | active_record/ActiveRecord.rb:42:3:47:5 | some_other_request_handler | | active_record/ActiveRecord.rb:49:3:63:5 | safe_paths | | active_record/ActiveRecord.rb:67:3:69:5 | yet_another_handler | -| active_record/ActiveRecord.rb:73:3:75:5 | index | -| active_record/ActiveRecord.rb:77:3:79:5 | unsafe_action | +| active_record/ActiveRecord.rb:71:3:73:5 | create1 | +| active_record/ActiveRecord.rb:75:3:77:5 | create2 | +| active_record/ActiveRecord.rb:79:3:81:5 | create3 | +| active_record/ActiveRecord.rb:83:3:85:5 | update1 | +| active_record/ActiveRecord.rb:87:3:89:5 | update2 | +| active_record/ActiveRecord.rb:91:3:93:5 | update3 | +| active_record/ActiveRecord.rb:97:3:99:5 | index | +| active_record/ActiveRecord.rb:101:3:103:5 | unsafe_action | | app/controllers/comments_controller.rb:2:3:3:5 | index | | app/controllers/comments_controller.rb:5:3:6:5 | show | | app/controllers/foo/bars_controller.rb:5:3:7:5 | index | @@ -41,7 +47,17 @@ paramsCalls | active_record/ActiveRecord.rb:59:12:59:17 | call to params | | active_record/ActiveRecord.rb:62:15:62:20 | call to params | | active_record/ActiveRecord.rb:68:21:68:26 | call to params | -| active_record/ActiveRecord.rb:78:59:78:64 | call to params | +| active_record/ActiveRecord.rb:72:18:72:23 | call to params | +| active_record/ActiveRecord.rb:76:24:76:29 | call to params | +| active_record/ActiveRecord.rb:76:49:76:54 | call to params | +| active_record/ActiveRecord.rb:80:25:80:30 | call to params | +| active_record/ActiveRecord.rb:80:50:80:55 | call to params | +| active_record/ActiveRecord.rb:84:21:84:26 | call to params | +| active_record/ActiveRecord.rb:88:27:88:32 | call to params | +| active_record/ActiveRecord.rb:88:52:88:57 | call to params | +| active_record/ActiveRecord.rb:92:28:92:33 | call to params | +| active_record/ActiveRecord.rb:92:53:92:58 | call to params | +| active_record/ActiveRecord.rb:102:59:102:64 | call to params | | app/controllers/foo/bars_controller.rb:13:21:13:26 | call to params | | app/controllers/foo/bars_controller.rb:14:10:14:15 | call to params | | app/controllers/foo/bars_controller.rb:21:21:21:26 | call to params | @@ -61,7 +77,17 @@ paramsSources | active_record/ActiveRecord.rb:59:12:59:17 | call to params | | active_record/ActiveRecord.rb:62:15:62:20 | call to params | | active_record/ActiveRecord.rb:68:21:68:26 | call to params | -| active_record/ActiveRecord.rb:78:59:78:64 | call to params | +| active_record/ActiveRecord.rb:72:18:72:23 | call to params | +| active_record/ActiveRecord.rb:76:24:76:29 | call to params | +| active_record/ActiveRecord.rb:76:49:76:54 | call to params | +| active_record/ActiveRecord.rb:80:25:80:30 | call to params | +| active_record/ActiveRecord.rb:80:50:80:55 | call to params | +| active_record/ActiveRecord.rb:84:21:84:26 | call to params | +| active_record/ActiveRecord.rb:88:27:88:32 | call to params | +| active_record/ActiveRecord.rb:88:52:88:57 | call to params | +| active_record/ActiveRecord.rb:92:28:92:33 | call to params | +| active_record/ActiveRecord.rb:92:53:92:58 | call to params | +| active_record/ActiveRecord.rb:102:59:102:64 | call to params | | app/controllers/foo/bars_controller.rb:13:21:13:26 | call to params | | app/controllers/foo/bars_controller.rb:14:10:14:15 | call to params | | app/controllers/foo/bars_controller.rb:21:21:21:26 | call to params | diff --git a/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.expected b/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.expected index b416d853440..d4022297559 100644 --- a/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.expected +++ b/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.expected @@ -22,7 +22,7 @@ activeRecordSqlExecutionRanges | ActiveRecord.rb:46:20:46:32 | ... + ... | | ActiveRecord.rb:52:16:52:28 | "name #{...}" | | ActiveRecord.rb:56:20:56:39 | "username = #{...}" | -| ActiveRecord.rb:78:27:78:76 | "this is an unsafe annotation:..." | +| ActiveRecord.rb:102:27:102:76 | "this is an unsafe annotation:..." | activeRecordModelClassMethodCalls | ActiveRecord.rb:2:3:2:17 | call to has_many | | ActiveRecord.rb:6:3:6:24 | call to belongs_to | @@ -45,8 +45,14 @@ activeRecordModelClassMethodCalls | ActiveRecord.rb:60:5:60:33 | call to find_by | | ActiveRecord.rb:62:5:62:34 | call to find | | ActiveRecord.rb:68:5:68:45 | call to delete_by | -| ActiveRecord.rb:74:13:74:54 | call to annotate | -| ActiveRecord.rb:78:13:78:77 | call to annotate | +| ActiveRecord.rb:72:5:72:24 | call to create | +| ActiveRecord.rb:76:5:76:66 | call to create | +| ActiveRecord.rb:80:5:80:68 | call to create | +| ActiveRecord.rb:84:5:84:27 | call to update | +| ActiveRecord.rb:88:5:88:69 | call to update | +| ActiveRecord.rb:92:5:92:71 | call to update | +| ActiveRecord.rb:98:13:98:54 | call to annotate | +| ActiveRecord.rb:102:13:102:77 | call to annotate | potentiallyUnsafeSqlExecutingMethodCall | ActiveRecord.rb:9:5:9:68 | call to find | | ActiveRecord.rb:19:5:19:25 | call to destroy_by | @@ -58,7 +64,7 @@ potentiallyUnsafeSqlExecutingMethodCall | ActiveRecord.rb:46:5:46:33 | call to delete_by | | ActiveRecord.rb:52:5:52:29 | call to order | | ActiveRecord.rb:56:7:56:40 | call to find_by | -| ActiveRecord.rb:78:13:78:77 | call to annotate | +| ActiveRecord.rb:102:13:102:77 | call to annotate | activeRecordModelInstantiations | ActiveRecord.rb:9:5:9:68 | call to find | ActiveRecord.rb:5:1:15:3 | User | | ActiveRecord.rb:13:5:13:40 | call to find_by | ActiveRecord.rb:1:1:3:3 | UserGroup | @@ -66,3 +72,13 @@ activeRecordModelInstantiations | ActiveRecord.rb:56:7:56:40 | call to find_by | ActiveRecord.rb:5:1:15:3 | User | | ActiveRecord.rb:60:5:60:33 | call to find_by | ActiveRecord.rb:5:1:15:3 | User | | ActiveRecord.rb:62:5:62:34 | call to find | ActiveRecord.rb:5:1:15:3 | User | +persistentWriteAccesses +| ActiveRecord.rb:72:5:72:24 | call to create | ActiveRecord.rb:72:18:72:23 | call to params | +| ActiveRecord.rb:76:5:76:66 | call to create | ActiveRecord.rb:76:24:76:36 | ...[...] | +| ActiveRecord.rb:76:5:76:66 | call to create | ActiveRecord.rb:76:49:76:65 | ...[...] | +| ActiveRecord.rb:80:5:80:68 | call to create | ActiveRecord.rb:80:25:80:37 | ...[...] | +| ActiveRecord.rb:80:5:80:68 | call to create | ActiveRecord.rb:80:50:80:66 | ...[...] | +| ActiveRecord.rb:84:5:84:27 | call to update | ActiveRecord.rb:84:21:84:26 | call to params | +| ActiveRecord.rb:88:5:88:69 | call to update | ActiveRecord.rb:88:27:88:39 | ...[...] | +| ActiveRecord.rb:88:5:88:69 | call to update | ActiveRecord.rb:88:52:88:68 | ...[...] | +| ActiveRecord.rb:92:5:92:71 | call to update | ActiveRecord.rb:92:21:92:70 | call to [] | diff --git a/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.rb b/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.rb index 561c4eff732..46df51deb43 100644 --- a/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.rb +++ b/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.rb @@ -73,15 +73,23 @@ class BazController < BarController end def create2 - Admin.create(name: params[:name]) + Admin.create(name: params[:name], password: params[:password]) + end + + def create3 + Admin.create({name: params[:name], password: params[:password]}) end def update1 - Admin.update(params) + Admin.update(1, params) end def update2 - Admin.update(name: params[:name]) + Admin.update(1, name: params[:name], password: params[:password]) + end + + def update3 + Admin.update(1, {name: params[:name], password: params[:password]}) end end From 452811dbf2caffe017c22d4d214911fdaa889fd1 Mon Sep 17 00:00:00 2001 From: Harry Maclean Date: Thu, 4 Aug 2022 17:25:55 +1200 Subject: [PATCH 437/505] Ruby: move change note --- ruby/ql/{src => lib}/change-notes/2022-08-04-mime-type.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ruby/ql/{src => lib}/change-notes/2022-08-04-mime-type.md (100%) diff --git a/ruby/ql/src/change-notes/2022-08-04-mime-type.md b/ruby/ql/lib/change-notes/2022-08-04-mime-type.md similarity index 100% rename from ruby/ql/src/change-notes/2022-08-04-mime-type.md rename to ruby/ql/lib/change-notes/2022-08-04-mime-type.md From ee9e6b1f2e03a4e8cf1c01729e8208df1f7e8218 Mon Sep 17 00:00:00 2001 From: Harry Maclean Date: Thu, 4 Aug 2022 17:27:34 +1200 Subject: [PATCH 438/505] Ruby: Add change note --- ruby/ql/lib/change-notes/2022-08-04-active-record-writes.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 ruby/ql/lib/change-notes/2022-08-04-active-record-writes.md diff --git a/ruby/ql/lib/change-notes/2022-08-04-active-record-writes.md b/ruby/ql/lib/change-notes/2022-08-04-active-record-writes.md new file mode 100644 index 00000000000..b2b4d0bc2ad --- /dev/null +++ b/ruby/ql/lib/change-notes/2022-08-04-active-record-writes.md @@ -0,0 +1,5 @@ +--- +category: minorAnalysis +--- +* Calls to `ActiveRecord::Base.create` and `ActiveRecord::Base.update` are now + recognised as write accesses. From e4c9f8a9a2e0e36d1576efa220c70309c7bc1740 Mon Sep 17 00:00:00 2001 From: mc <42146119+mchammer01@users.noreply.github.com> Date: Thu, 4 Aug 2022 10:05:52 +0100 Subject: [PATCH 439/505] Update docs/codeql/codeql-cli/exit-codes.rst --- docs/codeql/codeql-cli/exit-codes.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/codeql/codeql-cli/exit-codes.rst b/docs/codeql/codeql-cli/exit-codes.rst index 04d4d93dc07..86a1dfc12dd 100644 --- a/docs/codeql/codeql-cli/exit-codes.rst +++ b/docs/codeql/codeql-cli/exit-codes.rst @@ -71,4 +71,4 @@ Other ----- In the case of really severe problems within the JVM that runs ``codeql``, it might return a nonzero exit code of its own choosing. -This should only happen if something is severely wrong with the CodeQL installation, or a memory issue with the host system running the CodeQL process. For example, Unix systems may return `Exit Code 137` to indicate that the kernel has killed a process that CodeQL has started. One way to troubleshoot this issue is to modify your `--ram=` flag for the `codeql database analyze` step and re-run your workflow. +This should only happen if something is severely wrong with the CodeQL installation, or if there is a memory issue with the host system running the CodeQL process. For example, Unix systems may return `Exit Code 137` to indicate that the kernel has killed a process that CodeQL has started. One way to troubleshoot this is to modify your `--ram=` flag for the `codeql database analyze` step and re-run your workflow. From bc6a74b4ddeb077a44a73b134ef6ea0bc59d9d87 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 4 Aug 2022 10:02:21 +0200 Subject: [PATCH 440/505] C#: Disable CLR tracer Also remove old tracer configs, as we now use the Lua tracer. --- csharp/codeql-extractor.yml | 6 ------ .../Semmle.Extraction.CSharp/Extractor/Extractor.cs | 6 ------ .../Semmle.Extraction.CSharp/Extractor/Options.cs | 8 -------- csharp/extractor/Semmle.Extraction.Tests/Options.cs | 1 - 4 files changed, 21 deletions(-) diff --git a/csharp/codeql-extractor.yml b/csharp/codeql-extractor.yml index 5d498049a8a..4e1fa7934ce 100644 --- a/csharp/codeql-extractor.yml +++ b/csharp/codeql-extractor.yml @@ -4,12 +4,6 @@ version: 1.22.1 column_kind: "utf16" extra_env_vars: DOTNET_GENERATE_ASPNET_CERTIFICATE: "false" - COR_ENABLE_PROFILING: "1" - COR_PROFILER: "{A3C70A64-7D41-4A94-A3F6-FD47D9259997}" - COR_PROFILER_PATH_64: "${env.CODEQL_EXTRACTOR_CSHARP_ROOT}/tools/${env.CODEQL_PLATFORM}/clrtracer64${env.CODEQL_PLATFORM_DLL_EXTENSION}" - CORECLR_ENABLE_PROFILING: "1" - CORECLR_PROFILER: "{A3C70A64-7D41-4A94-A3F6-FD47D9259997}" - CORECLR_PROFILER_PATH_64: "${env.CODEQL_EXTRACTOR_CSHARP_ROOT}/tools/${env.CODEQL_PLATFORM}/clrtracer64${env.CODEQL_PLATFORM_DLL_EXTENSION}" file_types: - name: cs display_name: C# sources diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Extractor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Extractor.cs index 8d63c6288bf..ec4f44c21c7 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Extractor.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Extractor.cs @@ -99,12 +99,6 @@ namespace Semmle.Extraction.CSharp using var logger = MakeLogger(options.Verbosity, options.Console); - if (Environment.GetEnvironmentVariable("SEMMLE_CLRTRACER") == "1" && !options.ClrTracer) - { - logger.Log(Severity.Info, "Skipping extraction since already extracted from the CLR tracer"); - return ExitCode.Ok; - } - var canonicalPathCache = CanonicalPathCache.Create(logger, 1000); var pathTransformer = new PathTransformer(canonicalPathCache); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Options.cs b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Options.cs index 7b4e0e95ea3..c2d21d6a16a 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Options.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Options.cs @@ -27,11 +27,6 @@ namespace Semmle.Extraction.CSharp /// public IList CompilerArguments { get; } = new List(); - /// - /// Holds if the extractor was launched from the CLR tracer. - /// - public bool ClrTracer { get; private set; } = false; - /// /// Holds if assembly information should be prefixed to TRAP labels. /// @@ -87,9 +82,6 @@ namespace Semmle.Extraction.CSharp { switch (flag) { - case "clrtracer": - ClrTracer = value; - return true; case "assemblysensitivetrap": AssemblySensitiveTrap = value; return true; diff --git a/csharp/extractor/Semmle.Extraction.Tests/Options.cs b/csharp/extractor/Semmle.Extraction.Tests/Options.cs index 9b17320fbaa..2637eb3d5f4 100644 --- a/csharp/extractor/Semmle.Extraction.Tests/Options.cs +++ b/csharp/extractor/Semmle.Extraction.Tests/Options.cs @@ -29,7 +29,6 @@ namespace Semmle.Extraction.Tests Assert.True(options.Threads >= 1); Assert.Equal(Verbosity.Info, options.Verbosity); Assert.False(options.Console); - Assert.False(options.ClrTracer); Assert.False(options.PDB); Assert.False(options.Fast); Assert.Equal(TrapWriter.CompressionMode.Brotli, options.TrapCompression); From 64e86609045bd77add9f340888f34e73a1813fd1 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Thu, 4 Aug 2022 14:18:25 +0200 Subject: [PATCH 441/505] C#: Simplification of AspNetCoreRemoteFlowSourceMember. --- .../csharp/security/dataflow/flowsources/Remote.qll | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/csharp/ql/lib/semmle/code/csharp/security/dataflow/flowsources/Remote.qll b/csharp/ql/lib/semmle/code/csharp/security/dataflow/flowsources/Remote.qll index c45d764701e..241f16d06ed 100644 --- a/csharp/ql/lib/semmle/code/csharp/security/dataflow/flowsources/Remote.qll +++ b/csharp/ql/lib/semmle/code/csharp/security/dataflow/flowsources/Remote.qll @@ -172,19 +172,19 @@ class ActionMethodParameter extends RemoteFlowSource, DataFlow::ParameterNode { abstract class AspNetCoreRemoteFlowSource extends RemoteFlowSource { } /** - * Data flow for AST.NET Core. + * Data flow for ASP.NET Core. * * Flow is defined from any ASP.NET Core remote source object to any of its member * properties. */ -private class AspNetCoreRemoteFlowSourceMember extends TaintTracking::TaintedMember { +private class AspNetCoreRemoteFlowSourceMember extends TaintTracking::TaintedMember, Property { AspNetCoreRemoteFlowSourceMember() { this.getDeclaringType() = any(AspNetCoreRemoteFlowSource source).getType() and this.isPublic() and not this.isStatic() and - exists(Property p | p = this | - p.isAutoImplemented() and p.getGetter().isPublic() and p.getSetter().isPublic() - ) + this.isAutoImplemented() and + this.getGetter().isPublic() and + this.getSetter().isPublic() } } From 38ede25385608b3531afd01d5a314c8d74b8c882 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 4 Aug 2022 14:19:39 +0200 Subject: [PATCH 442/505] Ruby: Add test that illustrates missing flow for keyword arguments --- .../dataflow/params/params-flow.expected | 12 ++++++++++++ .../library-tests/dataflow/params/params_flow.rb | 14 ++++++++++---- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/ruby/ql/test/library-tests/dataflow/params/params-flow.expected b/ruby/ql/test/library-tests/dataflow/params/params-flow.expected index 3df4f046a07..79743085f1d 100644 --- a/ruby/ql/test/library-tests/dataflow/params/params-flow.expected +++ b/ruby/ql/test/library-tests/dataflow/params/params-flow.expected @@ -1,4 +1,6 @@ failures +| params_flow.rb:17:13:17:82 | # $ hasValueFlow=3 $ hasValueFlow=6 $ hasValueFlow=8 $ hasValueFlow=16 | Missing result:hasValueFlow=16 | +| params_flow.rb:26:13:26:66 | # $ hasValueFlow=9 $ hasValueFlow=13 $ hasValueFlow=14 | Missing result:hasValueFlow=14 | edges | params_flow.rb:9:16:9:17 | p1 : | params_flow.rb:10:10:10:11 | p1 | | params_flow.rb:9:20:9:21 | p2 : | params_flow.rb:11:10:11:11 | p2 | @@ -26,6 +28,10 @@ edges | params_flow.rb:35:12:35:20 | call to taint : | params_flow.rb:25:12:25:13 | p1 : | | params_flow.rb:35:23:35:28 | ** ... [element :p3] : | params_flow.rb:25:17:25:24 | **kwargs [element :p3] : | | params_flow.rb:35:25:35:28 | args [element :p3] : | params_flow.rb:35:23:35:28 | ** ... [element :p3] : | +| params_flow.rb:37:34:37:42 | call to taint : | params_flow.rb:38:10:38:13 | args [element :p2] : | +| params_flow.rb:38:8:38:13 | ** ... [element :p2] : | params_flow.rb:25:17:25:24 | **kwargs [element :p2] : | +| params_flow.rb:38:10:38:13 | args [element :p2] : | params_flow.rb:38:8:38:13 | ** ... [element :p2] : | +| params_flow.rb:41:13:41:21 | call to taint : | params_flow.rb:16:18:16:19 | p2 : | nodes | params_flow.rb:9:16:9:17 | p1 : | semmle.label | p1 : | | params_flow.rb:9:20:9:21 | p2 : | semmle.label | p2 : | @@ -60,6 +66,10 @@ nodes | params_flow.rb:35:12:35:20 | call to taint : | semmle.label | call to taint : | | params_flow.rb:35:23:35:28 | ** ... [element :p3] : | semmle.label | ** ... [element :p3] : | | params_flow.rb:35:25:35:28 | args [element :p3] : | semmle.label | args [element :p3] : | +| params_flow.rb:37:34:37:42 | call to taint : | semmle.label | call to taint : | +| params_flow.rb:38:8:38:13 | ** ... [element :p2] : | semmle.label | ** ... [element :p2] : | +| params_flow.rb:38:10:38:13 | args [element :p2] : | semmle.label | args [element :p2] : | +| params_flow.rb:41:13:41:21 | call to taint : | semmle.label | call to taint : | subpaths #select | params_flow.rb:10:10:10:11 | p1 | params_flow.rb:14:12:14:19 | call to taint : | params_flow.rb:10:10:10:11 | p1 | $@ | params_flow.rb:14:12:14:19 | call to taint : | call to taint : | @@ -70,8 +80,10 @@ subpaths | params_flow.rb:18:10:18:11 | p2 | params_flow.rb:21:27:21:34 | call to taint : | params_flow.rb:18:10:18:11 | p2 | $@ | params_flow.rb:21:27:21:34 | call to taint : | call to taint : | | params_flow.rb:18:10:18:11 | p2 | params_flow.rb:22:13:22:20 | call to taint : | params_flow.rb:18:10:18:11 | p2 | $@ | params_flow.rb:22:13:22:20 | call to taint : | call to taint : | | params_flow.rb:18:10:18:11 | p2 | params_flow.rb:23:16:23:23 | call to taint : | params_flow.rb:18:10:18:11 | p2 | $@ | params_flow.rb:23:16:23:23 | call to taint : | call to taint : | +| params_flow.rb:18:10:18:11 | p2 | params_flow.rb:41:13:41:21 | call to taint : | params_flow.rb:18:10:18:11 | p2 | $@ | params_flow.rb:41:13:41:21 | call to taint : | call to taint : | | params_flow.rb:26:10:26:11 | p1 | params_flow.rb:33:12:33:19 | call to taint : | params_flow.rb:26:10:26:11 | p1 | $@ | params_flow.rb:33:12:33:19 | call to taint : | call to taint : | | params_flow.rb:26:10:26:11 | p1 | params_flow.rb:35:12:35:20 | call to taint : | params_flow.rb:26:10:26:11 | p1 | $@ | params_flow.rb:35:12:35:20 | call to taint : | call to taint : | | params_flow.rb:28:10:28:22 | ( ... ) | params_flow.rb:33:26:33:34 | call to taint : | params_flow.rb:28:10:28:22 | ( ... ) | $@ | params_flow.rb:33:26:33:34 | call to taint : | call to taint : | +| params_flow.rb:28:10:28:22 | ( ... ) | params_flow.rb:37:34:37:42 | call to taint : | params_flow.rb:28:10:28:22 | ( ... ) | $@ | params_flow.rb:37:34:37:42 | call to taint : | call to taint : | | params_flow.rb:29:10:29:22 | ( ... ) | params_flow.rb:33:41:33:49 | call to taint : | params_flow.rb:29:10:29:22 | ( ... ) | $@ | params_flow.rb:33:41:33:49 | call to taint : | call to taint : | | params_flow.rb:29:10:29:22 | ( ... ) | params_flow.rb:34:14:34:22 | call to taint : | params_flow.rb:29:10:29:22 | ( ... ) | $@ | params_flow.rb:34:14:34:22 | call to taint : | call to taint : | diff --git a/ruby/ql/test/library-tests/dataflow/params/params_flow.rb b/ruby/ql/test/library-tests/dataflow/params/params_flow.rb index d3a99e61f37..a41706261e2 100644 --- a/ruby/ql/test/library-tests/dataflow/params/params_flow.rb +++ b/ruby/ql/test/library-tests/dataflow/params/params_flow.rb @@ -14,8 +14,8 @@ end positional(taint(1), taint(2)) def keyword(p1:, p2:) - sink p1 # $ hasValueFlow=3 $ hasValueFlow=6 $ hasValueFlow=8 - sink p2 # $ hasValueFlow=4 $ hasValueFlow=5 $ hasValueFlow=7 + sink p1 # $ hasValueFlow=3 $ hasValueFlow=6 $ hasValueFlow=8 $ hasValueFlow=16 + sink p2 # $ hasValueFlow=4 $ hasValueFlow=5 $ hasValueFlow=7 $ hasValueFlow=17 end keyword(p1: taint(3), p2: taint(4)) @@ -23,9 +23,9 @@ keyword(p2: taint(5), p1: taint(6)) keyword(:p2 => taint(7), :p1 => taint(8)) def kwargs(p1:, **kwargs) - sink p1 # $ hasValueFlow=9 $ hasValueFlow=13 + sink p1 # $ hasValueFlow=9 $ hasValueFlow=13 $ hasValueFlow=14 sink (kwargs[:p1]) - sink (kwargs[:p2]) # $ hasValueFlow=10 + sink (kwargs[:p2]) # $ hasValueFlow=10 $ hasValueFlow=15 sink (kwargs[:p3]) # $ hasValueFlow=11 $ hasValueFlow=12 sink (kwargs[:p4]) end @@ -33,3 +33,9 @@ end kwargs(p1: taint(9), p2: taint(10), p3: taint(11), p4: "") args = { p3: taint(12), p4: "" } kwargs(p1: taint(13), **args) + +args = {:p1 => taint(14), :p2 => taint(15) } +kwargs(**args) + +args = {:p1 => taint(16) } +keyword(p2: taint(17), **args) From 43d4324f65b0a3f56aeccef98c14d013607ba1c5 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Thu, 4 Aug 2022 16:05:30 +0200 Subject: [PATCH 443/505] Java: Improve performance of ConfusingOverloading. --- .../ConfusingOverloading.ql | 40 ++++++++++++++++--- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/java/ql/src/Violations of Best Practice/Naming Conventions/ConfusingOverloading.ql b/java/ql/src/Violations of Best Practice/Naming Conventions/ConfusingOverloading.ql index 726c2453f65..e3ac1384243 100644 --- a/java/ql/src/Violations of Best Practice/Naming Conventions/ConfusingOverloading.ql +++ b/java/ql/src/Violations of Best Practice/Naming Conventions/ConfusingOverloading.ql @@ -60,13 +60,43 @@ private predicate candidateMethod(RefType t, Method m, string name, int numParam not whitelist(name) } -pragma[inline] -private predicate potentiallyConfusingTypes(Type a, Type b) { - exists(RefType commonSubtype | hasSubtypeOrInstantiation*(a, commonSubtype) | - hasSubtypeOrInstantiation*(b, commonSubtype) +predicate paramTypePair(Type t1, Type t2) { + exists(Method n, Method m, int i | + overloadedMethodsMostSpecific(n, m) and + t1 = n.getParameterType(i) and + t2 = m.getParameterType(i) ) +} + +// handle simple cases separately +predicate potentiallyConfusingTypesSimple(Type t1, Type t2) { + paramTypePair(t1, t2) and + ( + t1 = t2 + or + t1 instanceof TypeObject and t2 instanceof RefType + or + t2 instanceof TypeObject and t1 instanceof RefType + or + confusingPrimitiveBoxedTypes(t1, t2) + ) +} + +// check erased types first +predicate potentiallyConfusingTypesRefTypes(RefType t1, RefType t2) { + paramTypePair(t1, t2) and + not potentiallyConfusingTypesSimple(t1, t2) and + haveIntersection(t1, t2) +} + +// then check hasSubtypeOrInstantiation +predicate potentiallyConfusingTypes(Type t1, Type t2) { + potentiallyConfusingTypesSimple(t1, t2) or - confusingPrimitiveBoxedTypes(a, b) + potentiallyConfusingTypesRefTypes(t1, t2) and + exists(RefType commonSubtype | hasSubtypeOrInstantiation*(t1, commonSubtype) | + hasSubtypeOrInstantiation*(t2, commonSubtype) + ) } private predicate hasSubtypeOrInstantiation(RefType t, RefType sub) { From 01c0d4b59f5a0a50ee6255735e7734c4604ac266 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 4 Aug 2022 15:19:56 +0200 Subject: [PATCH 444/505] Ruby: Support more flow through keyword arguments --- .../DataFlowConsistency.ql | 2 +- .../dataflow/internal/DataFlowPrivate.qll | 117 +++++++++++++++--- .../dataflow/params/params-flow.expected | 16 ++- 3 files changed, 112 insertions(+), 23 deletions(-) diff --git a/ruby/ql/consistency-queries/DataFlowConsistency.ql b/ruby/ql/consistency-queries/DataFlowConsistency.ql index 000bbc57899..1a891dd40f7 100644 --- a/ruby/ql/consistency-queries/DataFlowConsistency.ql +++ b/ruby/ql/consistency-queries/DataFlowConsistency.ql @@ -10,6 +10,6 @@ private class MyConsistencyConfiguration extends ConsistencyConfiguration { or n instanceof SummaryNode or - n instanceof HashSplatArgumentsNode + n instanceof SynthHashSplatArgumentsNode } } diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll index a6c73b1d893..b19894bf2d1 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll @@ -227,6 +227,7 @@ private module Cached { } or TSelfParameterNode(MethodBase m) or TBlockParameterNode(MethodBase m) or + TSynthHashSplatParameterNode(MethodBase m) { m.getAParameter() instanceof KeywordParameter } or TExprPostUpdateNode(CfgNodes::ExprCfgNode n) { n instanceof Argument or n = any(CfgNodes::ExprNodes::InstanceVariableAccessCfgNode v).getReceiver() @@ -240,12 +241,13 @@ private module Cached { TSummaryParameterNode(FlowSummaryImpl::Public::SummarizedCallable c, ParameterPosition pos) { FlowSummaryImpl::Private::summaryParameterNodeRange(c, pos) } or - THashSplatArgumentsNode(CfgNodes::ExprNodes::CallCfgNode c) { + TSynthHashSplatArgumentsNode(CfgNodes::ExprNodes::CallCfgNode c) { exists(Argument arg | arg.isArgumentOf(c, any(ArgumentPosition pos | pos.isKeyword(_)))) } class TParameterNode = - TNormalParameterNode or TBlockParameterNode or TSelfParameterNode or TSummaryParameterNode; + TNormalParameterNode or TBlockParameterNode or TSelfParameterNode or + TSynthHashSplatParameterNode or TSummaryParameterNode; private predicate defaultValueFlow(NamedParameter p, ExprNode e) { p.(OptionalParameter).getDefaultValue() = e.getExprNode().getExpr() @@ -328,18 +330,21 @@ private module Cached { cached predicate isLocalSourceNode(Node n) { - n instanceof ParameterNode - or - n instanceof PostUpdateNodes::ExprPostUpdateNode - or - // Nodes that can't be reached from another entry definition or expression. - not reachedFromExprOrEntrySsaDef(n) - or - // Ensure all entry SSA definitions are local sources -- for parameters, this - // is needed by type tracking. Note that when the parameter has a default value, - // it will be reachable from an expression (the default value) and therefore - // won't be caught by the rule above. - entrySsaDefinition(n) + not n instanceof SynthHashSplatParameterNode and + ( + n instanceof ParameterNode + or + n instanceof PostUpdateNodes::ExprPostUpdateNode + or + // Nodes that can't be reached from another entry definition or expression. + not reachedFromExprOrEntrySsaDef(n) + or + // Ensure all entry SSA definitions are local sources -- for parameters, this + // is needed by type tracking. Note that when the parameter has a default value, + // it will be reachable from an expression (the default value) and therefore + // won't be caught by the rule above. + entrySsaDefinition(n) + ) } cached @@ -415,7 +420,9 @@ predicate nodeIsHidden(Node n) { or n instanceof SynthReturnNode or - n instanceof HashSplatArgumentsNode + n instanceof SynthHashSplatParameterNode + or + n instanceof SynthHashSplatArgumentsNode } /** An SSA definition, viewed as a node in a data flow graph. */ @@ -570,6 +577,74 @@ private module ParameterNodes { } } + /** + * For all methods containing keyword parameters, we construct a synthesized + * (hidden) parameter node to contain all keyword arguments. This allows us + * to handle cases like + * + * ```rb + * def foo(p1:, p2:) + * sink(p1) + * sink(p2) + * end + * + * args = {:p1 => taint(1), :p2 => taint(2) } + * foo(**args) + * ``` + * + * by adding read steps out of the synthesized parameter node to the relevant + * keyword parameters. + * + * Note that this will introduce a bit of redundancy in cases like + * + * ```rb + * foo(p1: taint(1), p2: taint(2)) + * ``` + * + * where direct keyword matching is possible, since we construct a synthesized hash + * splat argument (`SynthHashSplatArgumentsNode`) at the call site, which means that + * `taint(1)` will flow into `p1` both via normal keyword matching and via the synthesized + * nodes (and similarly for `p2`). However, this redunancy is OK since + * (a) it means that type-tracking through keyword arguments also works in most cases, + * (b) read/store steps can be avoided when direct keyword matching is possible, and + * hence access path limits are not a concern, and + * (c) since the synthesized nodes are hidden, the reported data-flow paths will be + * collapsed anyway. + */ + class SynthHashSplatParameterNode extends ParameterNodeImpl, TSynthHashSplatParameterNode { + private MethodBase method; + + SynthHashSplatParameterNode() { this = TSynthHashSplatParameterNode(method) } + + final Callable getMethod() { result = method } + + /** + * Gets a keyword parameter that will be the result of reading `c` out of this + * synthesized node. + */ + NormalParameterNode getAKeywordParameter(ContentSet c) { + exists(KeywordParameter p | + p = result.getParameter() and + p = method.getAParameter() + | + c = getKeywordContent(p.getName()) or + c.isSingleton(TUnknownElementContent()) + ) + } + + override Parameter getParameter() { none() } + + override predicate isSourceParameterOf(Callable c, ParameterPosition pos) { + c = method and pos.isHashSplat() + } + + override CfgScope getCfgScope() { result = method } + + override Location getLocationImpl() { result = method.getLocation() } + + override string toStringImpl() { result = "**kwargs" } + } + /** A parameter for a library callable with a flow summary. */ class SummaryParameterNode extends ParameterNodeImpl, TSummaryParameterNode { private FlowSummaryImpl::Public::SummarizedCallable sc; @@ -689,10 +764,10 @@ private module ArgumentNodes { * part of the method signature, such that those cannot end up in the hash-splat * parameter. */ - class HashSplatArgumentsNode extends ArgumentNode, THashSplatArgumentsNode { + class SynthHashSplatArgumentsNode extends ArgumentNode, TSynthHashSplatArgumentsNode { CfgNodes::ExprNodes::CallCfgNode c; - HashSplatArgumentsNode() { this = THashSplatArgumentsNode(c) } + SynthHashSplatArgumentsNode() { this = TSynthHashSplatArgumentsNode(c) } override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) { this.sourceArgumentOf(call.asCall(), pos) @@ -704,10 +779,10 @@ private module ArgumentNodes { } } - private class HashSplatArgumentsNodeImpl extends NodeImpl, THashSplatArgumentsNode { + private class SynthHashSplatArgumentsNodeImpl extends NodeImpl, TSynthHashSplatArgumentsNode { CfgNodes::ExprNodes::CallCfgNode c; - HashSplatArgumentsNodeImpl() { this = THashSplatArgumentsNode(c) } + SynthHashSplatArgumentsNodeImpl() { this = TSynthHashSplatArgumentsNode(c) } override CfgScope getCfgScope() { result = c.getExpr().getCfgScope() } @@ -929,7 +1004,7 @@ predicate storeStep(Node node1, ContentSet c, Node node2) { or // Wrap all keyword arguments in a synthesized hash-splat argument node exists(CfgNodes::ExprNodes::CallCfgNode call, ArgumentPosition keywordPos, string name | - node2 = THashSplatArgumentsNode(call) and + node2 = TSynthHashSplatArgumentsNode(call) and node1.asExpr().(Argument).isArgumentOf(call, keywordPos) and keywordPos.isKeyword(name) and c = getKeywordContent(name) @@ -962,6 +1037,8 @@ predicate readStep(Node node1, ContentSet c, Node node2) { )) ) or + node2 = node1.(SynthHashSplatParameterNode).getAKeywordParameter(c) + or FlowSummaryImpl::Private::Steps::summaryReadStep(node1, c, node2) } diff --git a/ruby/ql/test/library-tests/dataflow/params/params-flow.expected b/ruby/ql/test/library-tests/dataflow/params/params-flow.expected index 79743085f1d..82840bcf95e 100644 --- a/ruby/ql/test/library-tests/dataflow/params/params-flow.expected +++ b/ruby/ql/test/library-tests/dataflow/params/params-flow.expected @@ -1,6 +1,4 @@ failures -| params_flow.rb:17:13:17:82 | # $ hasValueFlow=3 $ hasValueFlow=6 $ hasValueFlow=8 $ hasValueFlow=16 | Missing result:hasValueFlow=16 | -| params_flow.rb:26:13:26:66 | # $ hasValueFlow=9 $ hasValueFlow=13 $ hasValueFlow=14 | Missing result:hasValueFlow=14 | edges | params_flow.rb:9:16:9:17 | p1 : | params_flow.rb:10:10:10:11 | p1 | | params_flow.rb:9:20:9:21 | p2 : | params_flow.rb:11:10:11:11 | p2 | @@ -28,10 +26,16 @@ edges | params_flow.rb:35:12:35:20 | call to taint : | params_flow.rb:25:12:25:13 | p1 : | | params_flow.rb:35:23:35:28 | ** ... [element :p3] : | params_flow.rb:25:17:25:24 | **kwargs [element :p3] : | | params_flow.rb:35:25:35:28 | args [element :p3] : | params_flow.rb:35:23:35:28 | ** ... [element :p3] : | +| params_flow.rb:37:16:37:24 | call to taint : | params_flow.rb:38:10:38:13 | args [element :p1] : | | params_flow.rb:37:34:37:42 | call to taint : | params_flow.rb:38:10:38:13 | args [element :p2] : | +| params_flow.rb:38:8:38:13 | ** ... [element :p1] : | params_flow.rb:25:12:25:13 | p1 : | | params_flow.rb:38:8:38:13 | ** ... [element :p2] : | params_flow.rb:25:17:25:24 | **kwargs [element :p2] : | +| params_flow.rb:38:10:38:13 | args [element :p1] : | params_flow.rb:38:8:38:13 | ** ... [element :p1] : | | params_flow.rb:38:10:38:13 | args [element :p2] : | params_flow.rb:38:8:38:13 | ** ... [element :p2] : | +| params_flow.rb:40:16:40:24 | call to taint : | params_flow.rb:41:26:41:29 | args [element :p1] : | | params_flow.rb:41:13:41:21 | call to taint : | params_flow.rb:16:18:16:19 | p2 : | +| params_flow.rb:41:24:41:29 | ** ... [element :p1] : | params_flow.rb:16:13:16:14 | p1 : | +| params_flow.rb:41:26:41:29 | args [element :p1] : | params_flow.rb:41:24:41:29 | ** ... [element :p1] : | nodes | params_flow.rb:9:16:9:17 | p1 : | semmle.label | p1 : | | params_flow.rb:9:20:9:21 | p2 : | semmle.label | p2 : | @@ -66,10 +70,16 @@ nodes | params_flow.rb:35:12:35:20 | call to taint : | semmle.label | call to taint : | | params_flow.rb:35:23:35:28 | ** ... [element :p3] : | semmle.label | ** ... [element :p3] : | | params_flow.rb:35:25:35:28 | args [element :p3] : | semmle.label | args [element :p3] : | +| params_flow.rb:37:16:37:24 | call to taint : | semmle.label | call to taint : | | params_flow.rb:37:34:37:42 | call to taint : | semmle.label | call to taint : | +| params_flow.rb:38:8:38:13 | ** ... [element :p1] : | semmle.label | ** ... [element :p1] : | | params_flow.rb:38:8:38:13 | ** ... [element :p2] : | semmle.label | ** ... [element :p2] : | +| params_flow.rb:38:10:38:13 | args [element :p1] : | semmle.label | args [element :p1] : | | params_flow.rb:38:10:38:13 | args [element :p2] : | semmle.label | args [element :p2] : | +| params_flow.rb:40:16:40:24 | call to taint : | semmle.label | call to taint : | | params_flow.rb:41:13:41:21 | call to taint : | semmle.label | call to taint : | +| params_flow.rb:41:24:41:29 | ** ... [element :p1] : | semmle.label | ** ... [element :p1] : | +| params_flow.rb:41:26:41:29 | args [element :p1] : | semmle.label | args [element :p1] : | subpaths #select | params_flow.rb:10:10:10:11 | p1 | params_flow.rb:14:12:14:19 | call to taint : | params_flow.rb:10:10:10:11 | p1 | $@ | params_flow.rb:14:12:14:19 | call to taint : | call to taint : | @@ -77,12 +87,14 @@ subpaths | params_flow.rb:17:10:17:11 | p1 | params_flow.rb:21:13:21:20 | call to taint : | params_flow.rb:17:10:17:11 | p1 | $@ | params_flow.rb:21:13:21:20 | call to taint : | call to taint : | | params_flow.rb:17:10:17:11 | p1 | params_flow.rb:22:27:22:34 | call to taint : | params_flow.rb:17:10:17:11 | p1 | $@ | params_flow.rb:22:27:22:34 | call to taint : | call to taint : | | params_flow.rb:17:10:17:11 | p1 | params_flow.rb:23:33:23:40 | call to taint : | params_flow.rb:17:10:17:11 | p1 | $@ | params_flow.rb:23:33:23:40 | call to taint : | call to taint : | +| params_flow.rb:17:10:17:11 | p1 | params_flow.rb:40:16:40:24 | call to taint : | params_flow.rb:17:10:17:11 | p1 | $@ | params_flow.rb:40:16:40:24 | call to taint : | call to taint : | | params_flow.rb:18:10:18:11 | p2 | params_flow.rb:21:27:21:34 | call to taint : | params_flow.rb:18:10:18:11 | p2 | $@ | params_flow.rb:21:27:21:34 | call to taint : | call to taint : | | params_flow.rb:18:10:18:11 | p2 | params_flow.rb:22:13:22:20 | call to taint : | params_flow.rb:18:10:18:11 | p2 | $@ | params_flow.rb:22:13:22:20 | call to taint : | call to taint : | | params_flow.rb:18:10:18:11 | p2 | params_flow.rb:23:16:23:23 | call to taint : | params_flow.rb:18:10:18:11 | p2 | $@ | params_flow.rb:23:16:23:23 | call to taint : | call to taint : | | params_flow.rb:18:10:18:11 | p2 | params_flow.rb:41:13:41:21 | call to taint : | params_flow.rb:18:10:18:11 | p2 | $@ | params_flow.rb:41:13:41:21 | call to taint : | call to taint : | | params_flow.rb:26:10:26:11 | p1 | params_flow.rb:33:12:33:19 | call to taint : | params_flow.rb:26:10:26:11 | p1 | $@ | params_flow.rb:33:12:33:19 | call to taint : | call to taint : | | params_flow.rb:26:10:26:11 | p1 | params_flow.rb:35:12:35:20 | call to taint : | params_flow.rb:26:10:26:11 | p1 | $@ | params_flow.rb:35:12:35:20 | call to taint : | call to taint : | +| params_flow.rb:26:10:26:11 | p1 | params_flow.rb:37:16:37:24 | call to taint : | params_flow.rb:26:10:26:11 | p1 | $@ | params_flow.rb:37:16:37:24 | call to taint : | call to taint : | | params_flow.rb:28:10:28:22 | ( ... ) | params_flow.rb:33:26:33:34 | call to taint : | params_flow.rb:28:10:28:22 | ( ... ) | $@ | params_flow.rb:33:26:33:34 | call to taint : | call to taint : | | params_flow.rb:28:10:28:22 | ( ... ) | params_flow.rb:37:34:37:42 | call to taint : | params_flow.rb:28:10:28:22 | ( ... ) | $@ | params_flow.rb:37:34:37:42 | call to taint : | call to taint : | | params_flow.rb:29:10:29:22 | ( ... ) | params_flow.rb:33:41:33:49 | call to taint : | params_flow.rb:29:10:29:22 | ( ... ) | $@ | params_flow.rb:33:41:33:49 | call to taint : | call to taint : | From 3028b80e46daacfe11238dfba556d3f469856f5f Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Thu, 4 Aug 2022 20:01:05 +0100 Subject: [PATCH 445/505] Swift: Control-flow through interpolated strings. --- .../internal/ControlFlowGraphImpl.qll | 42 +++- .../controlflow/graph/Cfg.expected | 220 +++++++++++++++++- 2 files changed, 256 insertions(+), 6 deletions(-) diff --git a/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll b/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll index c3eda3a98f5..509c7b4efbd 100644 --- a/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll +++ b/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll @@ -96,21 +96,31 @@ module Stmts { override predicate propagatesAbnormal(ControlFlowElement node) { none() } + private predicate isBodyOfTapExpr() { any(TapExpr tap).getBody() = ast } + + // Note: If the brace statement is the body of a `TapExpr`, the first element is the variable + // declaration (see https://github.com/apple/swift/blob/main/include/swift/AST/Expr.h#L848) + // that's initialized by the `Tapxpr`. In `TapExprTre` we've already visited this declaration, + // along with its initializer. So we skip the first element here. + private AstNode getFirstElement() { + if this.isBodyOfTapExpr() then result = ast.getElement(1) else result = ast.getFirstElement() + } + override predicate first(ControlFlowElement first) { this.firstInner(first) or - not exists(ast.getFirstElement()) and first.asAstNode() = ast + not exists(this.getFirstElement()) and first.asAstNode() = ast } override predicate last(ControlFlowElement last, Completion c) { this.lastInner(last, c) or - not exists(ast.getFirstElement()) and + not exists(this.getFirstElement()) and last.asAstNode() = ast and c instanceof SimpleCompletion } - predicate firstInner(ControlFlowElement first) { astFirst(ast.getFirstElement(), first) } + predicate firstInner(ControlFlowElement first) { astFirst(this.getFirstElement(), first) } /** Gets the body of the i'th `defer` statement. */ private BraceStmt getDeferStmtBody(int i) { @@ -1334,10 +1344,34 @@ module Exprs { override InterpolatedStringLiteralExpr ast; final override ControlFlowElement getChildElement(int i) { - none() // TODO + i = 0 and + result.asAstNode() = ast.getAppendingExpr().getFullyConverted() } } + private class TapExprTree extends AstStandardPostOrderTree { + override TapExpr ast; + + final override ControlFlowElement getChildElement(int i) { + i = 0 and + result.asAstNode() = ast.getVar() + or + i = 1 and + result.asAstNode() = ast.getSubExpr().getFullyConverted() + or + // Note: The CFG for the body will skip the first element in the + // body because it's guarenteed to be the variable declaration + // that we've already visited at i = 0. See the explanation + // in `BraceStmtTree` for why this is necessary. + i = 2 and + result.asAstNode() = ast.getBody() + } + } + + private class OpaqueValueExprTree extends AstLeafTree { + override OpaqueValueExpr ast; + } + module DeclRefExprs { class DeclRefExprLValueTree extends AstLeafTree { override DeclRefExpr ast; diff --git a/swift/ql/test/library-tests/controlflow/graph/Cfg.expected b/swift/ql/test/library-tests/controlflow/graph/Cfg.expected index d14ec33b48e..e7ea8fc83b6 100644 --- a/swift/ql/test/library-tests/controlflow/graph/Cfg.expected +++ b/swift/ql/test/library-tests/controlflow/graph/Cfg.expected @@ -324,7 +324,6 @@ cfg.swift: #-----| -> error # 40| print(_:separator:terminator:) -#-----| -> "..." # 40| call to print(_:separator:terminator:) #-----| -> 0 @@ -341,12 +340,64 @@ cfg.swift: # 40| (Any) ... #-----| -> [...] +# 40| OpaqueValueExpr + +# 40| TapExpr +#-----| -> "..." + +# 40| Unknown error +#-----| -> call to ... + # 40| [...] #-----| -> default separator # 40| [...] #-----| -> [...] +# 40| call to ... +#-----| -> appendInterpolation(_:) + +# 40| $interpolation +#-----| -> &... + +# 40| &... +#-----| -> call to appendLiteral(_:) + +# 40| call to appendLiteral(_:) +#-----| -> Unknown error + +# 40| $interpolation +#-----| -> &... + +# 40| &... +#-----| -> call to appendInterpolation(_:) + +# 40| appendInterpolation(_:) +#-----| -> $interpolation + +# 40| call to appendInterpolation(_:) +#-----| -> error + +# 40| call to ... + +# 40| error +#-----| -> call to ... + +# 40| +#-----| -> call to ... + +# 40| $interpolation +#-----| -> &... + +# 40| &... +#-----| -> call to appendLiteral(_:) + +# 40| call to ... +#-----| -> TapExpr + +# 40| call to appendLiteral(_:) +#-----| -> + # 42| return ... #-----| return -> exit tryCatch(x:) (normal) @@ -2817,14 +2868,179 @@ cfg.swift: #-----| -> y # 262| y -#-----| -> "..." # 263| return ... #-----| return -> exit interpolatedString(x:y:) (normal) +# 263| +#-----| -> call to ... + # 263| "..." #-----| -> return ... +# 263| OpaqueValueExpr + +# 263| TapExpr +#-----| -> "..." + +# 263| call to ... +#-----| -> appendInterpolation(_:) + +# 263| $interpolation +#-----| -> &... + +# 263| &... +#-----| -> call to appendLiteral(_:) + +# 263| call to appendLiteral(_:) +#-----| -> + +# 263| $interpolation +#-----| -> &... + +# 263| &... +#-----| -> call to appendInterpolation(_:) + +# 263| appendInterpolation(_:) +#-----| -> $interpolation + +# 263| call to appendInterpolation(_:) +#-----| -> x + +# 263| call to ... + +# 263| x +#-----| -> call to ... + +# 263| + +#-----| -> call to ... + +# 263| $interpolation +#-----| -> &... + +# 263| &... +#-----| -> call to appendLiteral(_:) + +# 263| call to ... +#-----| -> appendInterpolation(_:) + +# 263| call to appendLiteral(_:) +#-----| -> + + +# 263| $interpolation +#-----| -> &... + +# 263| &... +#-----| -> call to appendInterpolation(_:) + +# 263| appendInterpolation(_:) +#-----| -> $interpolation + +# 263| call to appendInterpolation(_:) +#-----| -> y + +# 263| call to ... + +# 263| y +#-----| -> call to ... + +# 263| is equal to +#-----| -> call to ... + +# 263| $interpolation +#-----| -> &... + +# 263| &... +#-----| -> call to appendLiteral(_:) + +# 263| call to ... +#-----| -> appendInterpolation(_:) + +# 263| call to appendLiteral(_:) +#-----| -> is equal to + +# 263| $interpolation +#-----| -> &... + +# 263| &... +#-----| -> call to appendInterpolation(_:) + +# 263| appendInterpolation(_:) +#-----| -> $interpolation + +# 263| call to appendInterpolation(_:) +#-----| -> +(_:_:) + +# 263| call to ... + +# 263| x +#-----| -> y + +# 263| ... call to +(_:_:) ... +#-----| -> call to ... + +# 263| +(_:_:) +#-----| -> Int.Type + +# 263| Int.Type +#-----| -> call to +(_:_:) + +# 263| call to +(_:_:) +#-----| -> x + +# 263| y +#-----| -> ... call to +(_:_:) ... + +# 263| and here is a zero: +#-----| -> call to ... + +# 263| $interpolation +#-----| -> &... + +# 263| &... +#-----| -> call to appendLiteral(_:) + +# 263| call to ... +#-----| -> appendInterpolation(_:) + +# 263| call to appendLiteral(_:) +#-----| -> and here is a zero: + +# 263| $interpolation +#-----| -> &... + +# 263| &... +#-----| -> call to appendInterpolation(_:) + +# 263| appendInterpolation(_:) +#-----| -> $interpolation + +# 263| call to appendInterpolation(_:) +#-----| -> returnZero() + +# 263| call to ... + +# 263| returnZero() +#-----| -> call to returnZero() + +# 263| call to returnZero() +#-----| -> call to ... + +# 263| +#-----| -> call to ... + +# 263| $interpolation +#-----| -> &... + +# 263| &... +#-----| -> call to appendLiteral(_:) + +# 263| call to ... +#-----| -> TapExpr + +# 263| call to appendLiteral(_:) +#-----| -> + # 266| enter testSubscriptExpr() #-----| -> testSubscriptExpr() From ff6b8c5c9cb89c0a4aa0b03120897abd17684bf5 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Thu, 4 Aug 2022 20:02:36 +0100 Subject: [PATCH 446/505] Swift: Replace 'CallExpr' with 'ApplyExpr'. This is needed because not all the calls inside the interpolated string computations are 'CallExpr's. --- swift/ql/lib/codeql/swift/dataflow/Ssa.qll | 2 +- swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll | 2 +- swift/ql/lib/codeql/swift/dataflow/internal/SsaImplSpecific.qll | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/swift/ql/lib/codeql/swift/dataflow/Ssa.qll b/swift/ql/lib/codeql/swift/dataflow/Ssa.qll index 2805dff4637..81c4aeb6511 100644 --- a/swift/ql/lib/codeql/swift/dataflow/Ssa.qll +++ b/swift/ql/lib/codeql/swift/dataflow/Ssa.qll @@ -75,7 +75,7 @@ module Ssa { cached predicate isInoutDef(ExprCfgNode argument) { exists( - CallExpr c, BasicBlock bb, int blockIndex, int argIndex, VarDecl v, InOutExpr argExpr // TODO: use CFG node for assignment expr + ApplyExpr c, BasicBlock bb, int blockIndex, int argIndex, VarDecl v, InOutExpr argExpr // TODO: use CFG node for assignment expr | this.definesAt(v, bb, blockIndex) and bb.getNode(blockIndex).getNode().asAstNode() = c and diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll index e530df2fc20..9a639f125ac 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll @@ -203,7 +203,7 @@ abstract class ArgumentNode extends Node { private module ArgumentNodes { class NormalArgumentNode extends ExprNode, ArgumentNode { - NormalArgumentNode() { exists(CallExpr call | call.getAnArgument().getExpr() = this.asExpr()) } + NormalArgumentNode() { exists(ApplyExpr call | call.getAnArgument().getExpr() = this.asExpr()) } override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) { call.asCall().getArgument(pos.(PositionalArgumentPosition).getIndex()).getExpr() = diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/SsaImplSpecific.qll b/swift/ql/lib/codeql/swift/dataflow/internal/SsaImplSpecific.qll index 687e3b2cef8..2cd733d14c7 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/SsaImplSpecific.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/SsaImplSpecific.qll @@ -29,7 +29,7 @@ predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) certain = true ) or - exists(CallExpr call, Argument arg | + exists(ApplyExpr call, Argument arg | arg.getExpr().(InOutExpr).getSubExpr() = v.getAnAccess() and call.getAnArgument() = arg and bb.getNode(i).getNode().asAstNode() = call and From 52b78b6e68fd774036021008753f8d4d94f013c6 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Thu, 4 Aug 2022 20:03:36 +0100 Subject: [PATCH 447/505] Swift: Don't assume we know the call target statically in 'TInOutUpdateNode'. --- .../dataflow/internal/DataFlowPrivate.qll | 18 +++++------- .../codeql/swift/elements/expr/Argument.qll | 2 ++ .../dataflow/dataflow/DataFlow.expected | 28 +++++++++---------- .../dataflow/dataflow/LocalFlow.expected | 10 +++---- 4 files changed, 28 insertions(+), 30 deletions(-) diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll index 9a639f125ac..56977706e42 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll @@ -65,10 +65,7 @@ private module Cached { TExprNode(CfgNode n, Expr e) { hasExprNode(n, e) } or TSsaDefinitionNode(Ssa::Definition def) or TInoutReturnNode(ParamDecl param) { param.isInout() } or - TInOutUpdateNode(ParamDecl param, CallExpr call) { - param.isInout() and - call.getStaticTarget() = param.getDeclaringFunction() - } or + TInOutUpdateNode(Argument arg) { arg.getExpr() instanceof InOutExpr } or TSummaryNode(FlowSummary::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNodeState state) private predicate hasExprNode(CfgNode n, Expr e) { @@ -281,23 +278,22 @@ private module OutNodes { } class InOutUpdateNode extends OutNode, TInOutUpdateNode, NodeImpl { - ParamDecl param; - CallExpr call; + Argument arg; - InOutUpdateNode() { this = TInOutUpdateNode(param, call) } + InOutUpdateNode() { this = TInOutUpdateNode(arg) } override DataFlowCall getCall(ReturnKind kind) { - result.asCall().getExpr() = call and - kind.(ParamReturnKind).getIndex() = param.getIndex() + result.asCall().getExpr() = arg.getApplyExpr() and + kind.(ParamReturnKind).getIndex() = arg.getIndex() } override DataFlowCallable getEnclosingCallable() { result = this.getCall(_).getEnclosingCallable() } - override Location getLocationImpl() { result = call.getLocation() } + override Location getLocationImpl() { result = arg.getLocation() } - override string toStringImpl() { result = param.toString() } + override string toStringImpl() { result = arg.toString() } } } diff --git a/swift/ql/lib/codeql/swift/elements/expr/Argument.qll b/swift/ql/lib/codeql/swift/elements/expr/Argument.qll index 88c11b14013..bec912414a6 100644 --- a/swift/ql/lib/codeql/swift/elements/expr/Argument.qll +++ b/swift/ql/lib/codeql/swift/elements/expr/Argument.qll @@ -5,4 +5,6 @@ class Argument extends ArgumentBase { override string toString() { result = this.getLabel() + ": " + this.getExpr().toString() } int getIndex() { any(ApplyExpr apply).getArgument(result) = this } + + ApplyExpr getApplyExpr() { result.getAnArgument() = this } } diff --git a/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected b/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected index 1bcceb82f22..e07930e7ee4 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected +++ b/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected @@ -12,23 +12,23 @@ edges | test.swift:29:26:29:29 | y : | test.swift:31:15:31:15 | y | | test.swift:35:12:35:19 | call to source() : | test.swift:39:15:39:29 | call to callee_source() | | test.swift:43:19:43:26 | call to source() : | test.swift:50:15:50:15 | t | -| test.swift:53:1:56:1 | arg[return] : | test.swift:61:5:61:24 | arg : | +| test.swift:53:1:56:1 | arg[return] : | test.swift:61:17:61:23 | arg: &... : | | test.swift:54:11:54:18 | call to source() : | test.swift:53:1:56:1 | arg[return] : | -| test.swift:61:5:61:24 | arg : | test.swift:62:15:62:15 | x | +| test.swift:61:17:61:23 | arg: &... : | test.swift:62:15:62:15 | x | | test.swift:65:16:65:28 | WriteDef : | test.swift:65:1:70:1 | arg2[return] : | | test.swift:65:16:65:28 | arg1 : | test.swift:65:1:70:1 | arg2[return] : | | test.swift:73:18:73:25 | call to source() : | test.swift:75:21:75:22 | &... : | -| test.swift:75:5:75:33 | arg2 : | test.swift:77:15:77:15 | y | | test.swift:75:21:75:22 | &... : | test.swift:65:16:65:28 | WriteDef : | | test.swift:75:21:75:22 | &... : | test.swift:65:16:65:28 | arg1 : | -| test.swift:75:21:75:22 | &... : | test.swift:75:5:75:33 | arg2 : | -| test.swift:80:1:82:1 | arg[return] : | test.swift:97:9:97:41 | arg : | +| test.swift:75:21:75:22 | &... : | test.swift:75:25:75:32 | arg2: &... : | +| test.swift:75:25:75:32 | arg2: &... : | test.swift:77:15:77:15 | y | +| test.swift:80:1:82:1 | arg[return] : | test.swift:97:34:97:40 | arg: &... : | | test.swift:81:11:81:18 | call to source() : | test.swift:80:1:82:1 | arg[return] : | -| test.swift:84:1:91:1 | arg[return] : | test.swift:104:9:104:54 | arg : | +| test.swift:84:1:91:1 | arg[return] : | test.swift:104:35:104:41 | arg: &... : | | test.swift:86:15:86:22 | call to source() : | test.swift:84:1:91:1 | arg[return] : | | test.swift:89:15:89:22 | call to source() : | test.swift:84:1:91:1 | arg[return] : | -| test.swift:97:9:97:41 | arg : | test.swift:98:19:98:19 | x | -| test.swift:104:9:104:54 | arg : | test.swift:105:19:105:19 | x | +| test.swift:97:34:97:40 | arg: &... : | test.swift:98:19:98:19 | x | +| test.swift:104:35:104:41 | arg: &... : | test.swift:105:19:105:19 | x | | test.swift:109:9:109:14 | WriteDef : | test.swift:110:12:110:12 | arg : | | test.swift:109:9:109:14 | arg : | test.swift:110:12:110:12 | arg : | | test.swift:113:14:113:19 | WriteDef : | test.swift:114:19:114:19 | arg : | @@ -88,7 +88,7 @@ nodes | test.swift:50:15:50:15 | t | semmle.label | t | | test.swift:53:1:56:1 | arg[return] : | semmle.label | arg[return] : | | test.swift:54:11:54:18 | call to source() : | semmle.label | call to source() : | -| test.swift:61:5:61:24 | arg : | semmle.label | arg : | +| test.swift:61:17:61:23 | arg: &... : | semmle.label | arg: &... : | | test.swift:62:15:62:15 | x | semmle.label | x | | test.swift:65:1:70:1 | arg2[return] : | semmle.label | arg2[return] : | | test.swift:65:16:65:28 | WriteDef : | semmle.label | WriteDef : | @@ -96,17 +96,17 @@ nodes | test.swift:65:16:65:28 | arg1 : | semmle.label | WriteDef : | | test.swift:65:16:65:28 | arg1 : | semmle.label | arg1 : | | test.swift:73:18:73:25 | call to source() : | semmle.label | call to source() : | -| test.swift:75:5:75:33 | arg2 : | semmle.label | arg2 : | | test.swift:75:21:75:22 | &... : | semmle.label | &... : | +| test.swift:75:25:75:32 | arg2: &... : | semmle.label | arg2: &... : | | test.swift:77:15:77:15 | y | semmle.label | y | | test.swift:80:1:82:1 | arg[return] : | semmle.label | arg[return] : | | test.swift:81:11:81:18 | call to source() : | semmle.label | call to source() : | | test.swift:84:1:91:1 | arg[return] : | semmle.label | arg[return] : | | test.swift:86:15:86:22 | call to source() : | semmle.label | call to source() : | | test.swift:89:15:89:22 | call to source() : | semmle.label | call to source() : | -| test.swift:97:9:97:41 | arg : | semmle.label | arg : | +| test.swift:97:34:97:40 | arg: &... : | semmle.label | arg: &... : | | test.swift:98:19:98:19 | x | semmle.label | x | -| test.swift:104:9:104:54 | arg : | semmle.label | arg : | +| test.swift:104:35:104:41 | arg: &... : | semmle.label | arg: &... : | | test.swift:105:19:105:19 | x | semmle.label | x | | test.swift:109:9:109:14 | WriteDef : | semmle.label | WriteDef : | | test.swift:109:9:109:14 | WriteDef : | semmle.label | arg : | @@ -155,8 +155,8 @@ nodes | test.swift:157:16:157:23 | call to source() : | semmle.label | call to source() : | | test.swift:159:16:159:29 | call to ... : | semmle.label | call to ... : | subpaths -| test.swift:75:21:75:22 | &... : | test.swift:65:16:65:28 | WriteDef : | test.swift:65:1:70:1 | arg2[return] : | test.swift:75:5:75:33 | arg2 : | -| test.swift:75:21:75:22 | &... : | test.swift:65:16:65:28 | arg1 : | test.swift:65:1:70:1 | arg2[return] : | test.swift:75:5:75:33 | arg2 : | +| test.swift:75:21:75:22 | &... : | test.swift:65:16:65:28 | WriteDef : | test.swift:65:1:70:1 | arg2[return] : | test.swift:75:25:75:32 | arg2: &... : | +| test.swift:75:21:75:22 | &... : | test.swift:65:16:65:28 | arg1 : | test.swift:65:1:70:1 | arg2[return] : | test.swift:75:25:75:32 | arg2: &... : | | test.swift:114:19:114:19 | arg : | test.swift:109:9:109:14 | WriteDef : | test.swift:110:12:110:12 | arg : | test.swift:114:12:114:22 | call to ... : | | test.swift:114:19:114:19 | arg : | test.swift:109:9:109:14 | arg : | test.swift:110:12:110:12 | arg : | test.swift:114:12:114:22 | call to ... : | | test.swift:114:19:114:19 | arg : | test.swift:123:10:123:13 | WriteDef : | test.swift:124:16:124:16 | i : | test.swift:114:12:114:22 | call to ... : | diff --git a/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected b/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected index 54423a6d8d6..bceacff548e 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected +++ b/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected @@ -29,7 +29,7 @@ | test.swift:59:18:59:18 | 0 | test.swift:59:9:59:12 | WriteDef | | test.swift:60:15:60:15 | x | test.swift:61:23:61:23 | x | | test.swift:61:5:61:24 | WriteDef | test.swift:62:15:62:15 | x | -| test.swift:61:5:61:24 | arg | test.swift:61:5:61:24 | WriteDef | +| test.swift:61:17:61:23 | arg: &... | test.swift:61:5:61:24 | WriteDef | | test.swift:61:23:61:23 | x | test.swift:61:22:61:23 | &... | | test.swift:65:16:65:28 | WriteDef | test.swift:66:21:66:21 | arg1 | | test.swift:65:16:65:28 | arg1 | test.swift:66:21:66:21 | arg1 | @@ -47,9 +47,9 @@ | test.swift:74:18:74:18 | 0 | test.swift:74:9:74:12 | WriteDef | | test.swift:75:5:75:33 | WriteDef | test.swift:76:15:76:15 | x | | test.swift:75:5:75:33 | WriteDef | test.swift:77:15:77:15 | y | -| test.swift:75:5:75:33 | arg1 | test.swift:75:5:75:33 | WriteDef | -| test.swift:75:5:75:33 | arg2 | test.swift:75:5:75:33 | WriteDef | +| test.swift:75:15:75:22 | arg1: &... | test.swift:75:5:75:33 | WriteDef | | test.swift:75:22:75:22 | x | test.swift:75:21:75:22 | &... | +| test.swift:75:25:75:32 | arg2: &... | test.swift:75:5:75:33 | WriteDef | | test.swift:75:32:75:32 | y | test.swift:75:31:75:32 | &... | | test.swift:81:5:81:18 | WriteDef | test.swift:80:1:82:1 | arg[return] | | test.swift:81:11:81:18 | call to source() | test.swift:81:5:81:18 | WriteDef | @@ -66,13 +66,13 @@ | test.swift:95:22:95:22 | 0 | test.swift:95:13:95:16 | WriteDef | | test.swift:96:19:96:19 | x | test.swift:97:40:97:40 | x | | test.swift:97:9:97:41 | WriteDef | test.swift:98:19:98:19 | x | -| test.swift:97:9:97:41 | arg | test.swift:97:9:97:41 | WriteDef | +| test.swift:97:34:97:40 | arg: &... | test.swift:97:9:97:41 | WriteDef | | test.swift:97:40:97:40 | x | test.swift:97:39:97:40 | &... | | test.swift:102:13:102:16 | WriteDef | test.swift:103:19:103:19 | x | | test.swift:102:22:102:22 | 0 | test.swift:102:13:102:16 | WriteDef | | test.swift:103:19:103:19 | x | test.swift:104:41:104:41 | x | | test.swift:104:9:104:54 | WriteDef | test.swift:105:19:105:19 | x | -| test.swift:104:9:104:54 | arg | test.swift:104:9:104:54 | WriteDef | +| test.swift:104:35:104:41 | arg: &... | test.swift:104:9:104:54 | WriteDef | | test.swift:104:41:104:41 | x | test.swift:104:40:104:41 | &... | | test.swift:109:9:109:14 | WriteDef | test.swift:110:12:110:12 | arg | | test.swift:109:9:109:14 | arg | test.swift:110:12:110:12 | arg | From 9c48ce1bf2fdd13dff340fa9471b9ec644d32134 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Thu, 4 Aug 2022 20:06:10 +0100 Subject: [PATCH 448/505] Swift: Flow (1) through the internal function calls generated by the compiler during string interpolation, and (2) out of the internal 'TapExpr' and into the interpolated string result. --- .../dataflow/internal/SsaImplSpecific.qll | 14 ++++++++++++++ .../internal/TaintTrackingPrivate.qll | 19 ++++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/SsaImplSpecific.qll b/swift/ql/lib/codeql/swift/dataflow/internal/SsaImplSpecific.qll index 2cd733d14c7..2f507b31645 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/SsaImplSpecific.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/SsaImplSpecific.qll @@ -39,6 +39,13 @@ predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) v instanceof ParamDecl and bb.getNode(i).getNode().asAstNode() = v and certain = true + or + // Mark the subexpression as a write of the local variable declared in the `TapExpr`. + exists(TapExpr tap | + v = tap.getVar() and + bb.getNode(i).getNode().asAstNode() = tap.getSubExpr() and + certain = true + ) } private predicate isLValue(DeclRefExpr ref) { any(AssignExpr assign).getDest() = ref } @@ -58,4 +65,11 @@ predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain) bb.getScope() = func and certain = true ) + or + // Mark the `TapExpr` as a read of the of the local variable. + exists(TapExpr tap | + v = tap.getVar() and + bb.getNode(i).getNode().asAstNode() = tap and + certain = true + ) } diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/TaintTrackingPrivate.qll b/swift/ql/lib/codeql/swift/dataflow/internal/TaintTrackingPrivate.qll index 17cf1305ea1..804bada88bf 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/TaintTrackingPrivate.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/TaintTrackingPrivate.qll @@ -2,6 +2,8 @@ private import swift private import DataFlowPrivate private import TaintTrackingPublic private import codeql.swift.dataflow.DataFlow +private import codeql.swift.dataflow.Ssa +private import codeql.swift.controlflow.CfgNodes /** * Holds if `node` should be a sanitizer in all global taint flow configurations @@ -16,7 +18,22 @@ private module Cached { * in all global taint flow configurations. */ cached - predicate defaultAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { none() } + predicate defaultAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + // Flow through one argument of `appendLiteral` and `appendInterpolation` and to the second argument. + exists(ApplyExpr apply1, ApplyExpr apply2, ExprCfgNode e | + nodeFrom.asExpr() = [apply1, apply2].getAnArgument().getExpr() and + apply1.getFunction() = apply2 and + apply2.getStaticTarget().getName() = ["appendLiteral(_:)", "appendInterpolation(_:)"] and + e.getExpr() = apply2.getAnArgument().getExpr() and + nodeTo.asDefinition().(Ssa::WriteDefinition).isInoutDef(e) + ) + or + // Flow from the computation of the interpolated string literal to the result of the interpolation. + exists(InterpolatedStringLiteralExpr interpolated | + nodeTo.asExpr() = interpolated and + nodeFrom.asExpr() = interpolated.getAppendingExpr() + ) + } /** * Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local From 05e6dd85d4e3587c7a686a2804214e7a01e499d0 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Thu, 4 Aug 2022 20:06:48 +0100 Subject: [PATCH 449/505] Swift: Add taint tests for flow through interpolated strings. --- .../dataflow/taint/LocalTaint.expected | 121 ++++++++++++++++++ .../dataflow/taint/LocalTaint.ql | 6 + .../dataflow/taint/Taint.expected | 20 +++ .../library-tests/dataflow/taint/Taint.ql | 29 +++++ .../library-tests/dataflow/taint/test.swift | 22 ++++ 5 files changed, 198 insertions(+) create mode 100644 swift/ql/test/library-tests/dataflow/taint/LocalTaint.expected create mode 100644 swift/ql/test/library-tests/dataflow/taint/LocalTaint.ql create mode 100644 swift/ql/test/library-tests/dataflow/taint/Taint.expected create mode 100644 swift/ql/test/library-tests/dataflow/taint/Taint.ql create mode 100644 swift/ql/test/library-tests/dataflow/taint/test.swift diff --git a/swift/ql/test/library-tests/dataflow/taint/LocalTaint.expected b/swift/ql/test/library-tests/dataflow/taint/LocalTaint.expected new file mode 100644 index 00000000000..6796c2e8c40 --- /dev/null +++ b/swift/ql/test/library-tests/dataflow/taint/LocalTaint.expected @@ -0,0 +1,121 @@ +| file://:0:0:0:0 | Phi | test.swift:7:14:7:14 | $interpolation | +| file://:0:0:0:0 | Phi | test.swift:9:14:9:14 | $interpolation | +| file://:0:0:0:0 | Phi | test.swift:11:14:11:14 | $interpolation | +| file://:0:0:0:0 | Phi | test.swift:14:14:14:14 | $interpolation | +| file://:0:0:0:0 | Phi | test.swift:16:14:16:14 | $interpolation | +| file://:0:0:0:0 | Phi | test.swift:18:14:18:14 | $interpolation | +| file://:0:0:0:0 | Phi | test.swift:21:14:21:14 | $interpolation | +| test.swift:5:7:5:7 | WriteDef | test.swift:7:16:7:16 | x | +| test.swift:5:11:5:18 | call to source() | test.swift:5:7:5:7 | WriteDef | +| test.swift:7:13:7:13 | WriteDef | file://:0:0:0:0 | Phi | +| test.swift:7:14:7:14 | $interpolation | test.swift:7:14:7:14 | &... | +| test.swift:7:14:7:14 | : &... | test.swift:7:14:7:14 | WriteDef | +| test.swift:7:14:7:14 | WriteDef | test.swift:7:15:7:15 | $interpolation | +| test.swift:7:15:7:15 | $interpolation | test.swift:7:15:7:15 | &... | +| test.swift:7:15:7:15 | : &... | test.swift:7:15:7:15 | WriteDef | +| test.swift:7:15:7:15 | WriteDef | test.swift:7:18:7:18 | $interpolation | +| test.swift:7:16:7:16 | x | test.swift:9:16:9:16 | x | +| test.swift:7:18:7:18 | $interpolation | test.swift:7:18:7:18 | &... | +| test.swift:7:18:7:18 | : &... | test.swift:7:18:7:18 | WriteDef | +| test.swift:7:18:7:18 | WriteDef | test.swift:7:13:7:13 | TapExpr | +| test.swift:9:13:9:13 | WriteDef | file://:0:0:0:0 | Phi | +| test.swift:9:14:9:14 | $interpolation | test.swift:9:14:9:14 | &... | +| test.swift:9:14:9:14 | : &... | test.swift:9:14:9:14 | WriteDef | +| test.swift:9:14:9:14 | WriteDef | test.swift:9:15:9:15 | $interpolation | +| test.swift:9:15:9:15 | $interpolation | test.swift:9:15:9:15 | &... | +| test.swift:9:15:9:15 | : &... | test.swift:9:15:9:15 | WriteDef | +| test.swift:9:15:9:15 | WriteDef | test.swift:9:18:9:18 | $interpolation | +| test.swift:9:16:9:16 | x | test.swift:9:21:9:21 | x | +| test.swift:9:18:9:18 | $interpolation | test.swift:9:18:9:18 | &... | +| test.swift:9:18:9:18 | : &... | test.swift:9:18:9:18 | WriteDef | +| test.swift:9:18:9:18 | WriteDef | test.swift:9:20:9:20 | $interpolation | +| test.swift:9:20:9:20 | $interpolation | test.swift:9:20:9:20 | &... | +| test.swift:9:20:9:20 | : &... | test.swift:9:20:9:20 | WriteDef | +| test.swift:9:20:9:20 | WriteDef | test.swift:9:23:9:23 | $interpolation | +| test.swift:9:21:9:21 | x | test.swift:11:16:11:16 | x | +| test.swift:9:23:9:23 | $interpolation | test.swift:9:23:9:23 | &... | +| test.swift:9:23:9:23 | : &... | test.swift:9:23:9:23 | WriteDef | +| test.swift:9:23:9:23 | WriteDef | test.swift:9:13:9:13 | TapExpr | +| test.swift:11:13:11:13 | WriteDef | file://:0:0:0:0 | Phi | +| test.swift:11:14:11:14 | $interpolation | test.swift:11:14:11:14 | &... | +| test.swift:11:14:11:14 | : &... | test.swift:11:14:11:14 | WriteDef | +| test.swift:11:14:11:14 | WriteDef | test.swift:11:15:11:15 | $interpolation | +| test.swift:11:15:11:15 | $interpolation | test.swift:11:15:11:15 | &... | +| test.swift:11:15:11:15 | : &... | test.swift:11:15:11:15 | WriteDef | +| test.swift:11:15:11:15 | WriteDef | test.swift:11:18:11:18 | $interpolation | +| test.swift:11:16:11:16 | x | test.swift:11:26:11:26 | x | +| test.swift:11:18:11:18 | $interpolation | test.swift:11:18:11:18 | &... | +| test.swift:11:18:11:18 | : &... | test.swift:11:18:11:18 | WriteDef | +| test.swift:11:18:11:18 | WriteDef | test.swift:11:20:11:20 | $interpolation | +| test.swift:11:20:11:20 | $interpolation | test.swift:11:20:11:20 | &... | +| test.swift:11:20:11:20 | : &... | test.swift:11:20:11:20 | WriteDef | +| test.swift:11:20:11:20 | WriteDef | test.swift:11:23:11:23 | $interpolation | +| test.swift:11:23:11:23 | $interpolation | test.swift:11:23:11:23 | &... | +| test.swift:11:23:11:23 | : &... | test.swift:11:23:11:23 | WriteDef | +| test.swift:11:23:11:23 | WriteDef | test.swift:11:25:11:25 | $interpolation | +| test.swift:11:25:11:25 | $interpolation | test.swift:11:25:11:25 | &... | +| test.swift:11:25:11:25 | : &... | test.swift:11:25:11:25 | WriteDef | +| test.swift:11:25:11:25 | WriteDef | test.swift:11:28:11:28 | $interpolation | +| test.swift:11:26:11:26 | x | test.swift:16:16:16:16 | x | +| test.swift:11:28:11:28 | $interpolation | test.swift:11:28:11:28 | &... | +| test.swift:11:28:11:28 | : &... | test.swift:11:28:11:28 | WriteDef | +| test.swift:11:28:11:28 | WriteDef | test.swift:11:13:11:13 | TapExpr | +| test.swift:13:7:13:7 | WriteDef | test.swift:14:16:14:16 | y | +| test.swift:13:11:13:11 | 42 | test.swift:13:7:13:7 | WriteDef | +| test.swift:14:13:14:13 | WriteDef | file://:0:0:0:0 | Phi | +| test.swift:14:14:14:14 | $interpolation | test.swift:14:14:14:14 | &... | +| test.swift:14:14:14:14 | : &... | test.swift:14:14:14:14 | WriteDef | +| test.swift:14:14:14:14 | WriteDef | test.swift:14:15:14:15 | $interpolation | +| test.swift:14:15:14:15 | $interpolation | test.swift:14:15:14:15 | &... | +| test.swift:14:15:14:15 | : &... | test.swift:14:15:14:15 | WriteDef | +| test.swift:14:15:14:15 | WriteDef | test.swift:14:18:14:18 | $interpolation | +| test.swift:14:16:14:16 | y | test.swift:16:27:16:27 | y | +| test.swift:14:18:14:18 | $interpolation | test.swift:14:18:14:18 | &... | +| test.swift:14:18:14:18 | : &... | test.swift:14:18:14:18 | WriteDef | +| test.swift:14:18:14:18 | WriteDef | test.swift:14:13:14:13 | TapExpr | +| test.swift:16:13:16:13 | WriteDef | file://:0:0:0:0 | Phi | +| test.swift:16:14:16:14 | $interpolation | test.swift:16:14:16:14 | &... | +| test.swift:16:14:16:14 | : &... | test.swift:16:14:16:14 | WriteDef | +| test.swift:16:14:16:14 | WriteDef | test.swift:16:15:16:15 | $interpolation | +| test.swift:16:15:16:15 | $interpolation | test.swift:16:15:16:15 | &... | +| test.swift:16:15:16:15 | : &... | test.swift:16:15:16:15 | WriteDef | +| test.swift:16:15:16:15 | WriteDef | test.swift:16:18:16:18 | $interpolation | +| test.swift:16:16:16:16 | x | test.swift:18:27:18:27 | x | +| test.swift:16:18:16:18 | $interpolation | test.swift:16:18:16:18 | &... | +| test.swift:16:18:16:18 | : &... | test.swift:16:18:16:18 | WriteDef | +| test.swift:16:18:16:18 | WriteDef | test.swift:16:26:16:26 | $interpolation | +| test.swift:16:26:16:26 | $interpolation | test.swift:16:26:16:26 | &... | +| test.swift:16:26:16:26 | : &... | test.swift:16:26:16:26 | WriteDef | +| test.swift:16:26:16:26 | WriteDef | test.swift:16:29:16:29 | $interpolation | +| test.swift:16:27:16:27 | y | test.swift:18:16:18:16 | y | +| test.swift:16:29:16:29 | $interpolation | test.swift:16:29:16:29 | &... | +| test.swift:16:29:16:29 | : &... | test.swift:16:29:16:29 | WriteDef | +| test.swift:16:29:16:29 | WriteDef | test.swift:16:13:16:13 | TapExpr | +| test.swift:18:13:18:13 | WriteDef | file://:0:0:0:0 | Phi | +| test.swift:18:14:18:14 | $interpolation | test.swift:18:14:18:14 | &... | +| test.swift:18:14:18:14 | : &... | test.swift:18:14:18:14 | WriteDef | +| test.swift:18:14:18:14 | WriteDef | test.swift:18:15:18:15 | $interpolation | +| test.swift:18:15:18:15 | $interpolation | test.swift:18:15:18:15 | &... | +| test.swift:18:15:18:15 | : &... | test.swift:18:15:18:15 | WriteDef | +| test.swift:18:15:18:15 | WriteDef | test.swift:18:18:18:18 | $interpolation | +| test.swift:18:18:18:18 | $interpolation | test.swift:18:18:18:18 | &... | +| test.swift:18:18:18:18 | : &... | test.swift:18:18:18:18 | WriteDef | +| test.swift:18:18:18:18 | WriteDef | test.swift:18:26:18:26 | $interpolation | +| test.swift:18:26:18:26 | $interpolation | test.swift:18:26:18:26 | &... | +| test.swift:18:26:18:26 | : &... | test.swift:18:26:18:26 | WriteDef | +| test.swift:18:26:18:26 | WriteDef | test.swift:18:29:18:29 | $interpolation | +| test.swift:18:29:18:29 | $interpolation | test.swift:18:29:18:29 | &... | +| test.swift:18:29:18:29 | : &... | test.swift:18:29:18:29 | WriteDef | +| test.swift:18:29:18:29 | WriteDef | test.swift:18:13:18:13 | TapExpr | +| test.swift:20:3:20:7 | WriteDef | test.swift:21:16:21:16 | x | +| test.swift:20:7:20:7 | 0 | test.swift:20:3:20:7 | WriteDef | +| test.swift:21:13:21:13 | WriteDef | file://:0:0:0:0 | Phi | +| test.swift:21:14:21:14 | $interpolation | test.swift:21:14:21:14 | &... | +| test.swift:21:14:21:14 | : &... | test.swift:21:14:21:14 | WriteDef | +| test.swift:21:14:21:14 | WriteDef | test.swift:21:15:21:15 | $interpolation | +| test.swift:21:15:21:15 | $interpolation | test.swift:21:15:21:15 | &... | +| test.swift:21:15:21:15 | : &... | test.swift:21:15:21:15 | WriteDef | +| test.swift:21:15:21:15 | WriteDef | test.swift:21:18:21:18 | $interpolation | +| test.swift:21:18:21:18 | $interpolation | test.swift:21:18:21:18 | &... | +| test.swift:21:18:21:18 | : &... | test.swift:21:18:21:18 | WriteDef | +| test.swift:21:18:21:18 | WriteDef | test.swift:21:13:21:13 | TapExpr | diff --git a/swift/ql/test/library-tests/dataflow/taint/LocalTaint.ql b/swift/ql/test/library-tests/dataflow/taint/LocalTaint.ql new file mode 100644 index 00000000000..04a547b939d --- /dev/null +++ b/swift/ql/test/library-tests/dataflow/taint/LocalTaint.ql @@ -0,0 +1,6 @@ +import swift +import codeql.swift.dataflow.DataFlow + +from DataFlow::Node pred, DataFlow::Node succ +where DataFlow::localFlowStep(pred, succ) +select pred, succ diff --git a/swift/ql/test/library-tests/dataflow/taint/Taint.expected b/swift/ql/test/library-tests/dataflow/taint/Taint.expected new file mode 100644 index 00000000000..fea536d78c0 --- /dev/null +++ b/swift/ql/test/library-tests/dataflow/taint/Taint.expected @@ -0,0 +1,20 @@ +edges +| test.swift:5:11:5:18 | call to source() : | test.swift:7:13:7:13 | "..." | +| test.swift:5:11:5:18 | call to source() : | test.swift:9:13:9:13 | "..." | +| test.swift:5:11:5:18 | call to source() : | test.swift:11:13:11:13 | "..." | +| test.swift:5:11:5:18 | call to source() : | test.swift:16:13:16:13 | "..." | +| test.swift:5:11:5:18 | call to source() : | test.swift:18:13:18:13 | "..." | +nodes +| test.swift:5:11:5:18 | call to source() : | semmle.label | call to source() : | +| test.swift:7:13:7:13 | "..." | semmle.label | "..." | +| test.swift:9:13:9:13 | "..." | semmle.label | "..." | +| test.swift:11:13:11:13 | "..." | semmle.label | "..." | +| test.swift:16:13:16:13 | "..." | semmle.label | "..." | +| test.swift:18:13:18:13 | "..." | semmle.label | "..." | +subpaths +#select +| test.swift:7:13:7:13 | "..." | test.swift:5:11:5:18 | call to source() : | test.swift:7:13:7:13 | "..." | result | +| test.swift:9:13:9:13 | "..." | test.swift:5:11:5:18 | call to source() : | test.swift:9:13:9:13 | "..." | result | +| test.swift:11:13:11:13 | "..." | test.swift:5:11:5:18 | call to source() : | test.swift:11:13:11:13 | "..." | result | +| test.swift:16:13:16:13 | "..." | test.swift:5:11:5:18 | call to source() : | test.swift:16:13:16:13 | "..." | result | +| test.swift:18:13:18:13 | "..." | test.swift:5:11:5:18 | call to source() : | test.swift:18:13:18:13 | "..." | result | diff --git a/swift/ql/test/library-tests/dataflow/taint/Taint.ql b/swift/ql/test/library-tests/dataflow/taint/Taint.ql new file mode 100644 index 00000000000..35836c4cffc --- /dev/null +++ b/swift/ql/test/library-tests/dataflow/taint/Taint.ql @@ -0,0 +1,29 @@ +/** + * @kind path-problem + */ + +import swift +import codeql.swift.dataflow.TaintTracking +import codeql.swift.dataflow.DataFlow::DataFlow +import PathGraph + +class TestConfiguration extends TaintTracking::Configuration { + TestConfiguration() { this = "TestConfiguration" } + + override predicate isSource(Node src) { + src.asExpr().(CallExpr).getStaticTarget().getName() = "source()" + } + + override predicate isSink(Node sink) { + exists(CallExpr sinkCall | + sinkCall.getStaticTarget().getName() = "sink(arg:)" and + sinkCall.getAnArgument().getExpr() = sink.asExpr() + ) + } + + override int explorationLimit() { result = 100 } +} + +from PathNode src, PathNode sink, TestConfiguration test +where test.hasFlowPath(src, sink) +select sink, src, sink, "result" diff --git a/swift/ql/test/library-tests/dataflow/taint/test.swift b/swift/ql/test/library-tests/dataflow/taint/test.swift new file mode 100644 index 00000000000..36ff201788b --- /dev/null +++ b/swift/ql/test/library-tests/dataflow/taint/test.swift @@ -0,0 +1,22 @@ +func source() -> Int { return 0; } +func sink(arg: String) {} + +func taintThroughInterpolatedStrings() { + var x = source() + + sink(arg: "\(x)") // tainted + + sink(arg: "\(x) \(x)") // tainted + + sink(arg: "\(x) \(0) \(x)") // tainted + + var y = 42 + sink(arg: "\(y)") // clean + + sink(arg: "\(x) hello \(y)") // tainted + + sink(arg: "\(y) world \(x)") // tainted + + x = 0 + sink(arg: "\(x)") // clean +} \ No newline at end of file From 2f13c65ad71b94ef61f95a1f25747ba5743cd65d Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Thu, 4 Aug 2022 22:45:45 +0100 Subject: [PATCH 450/505] Update swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll Co-authored-by: intrigus-lgtm <60750685+intrigus-lgtm@users.noreply.github.com> --- .../codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll b/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll index 509c7b4efbd..6549d9692f2 100644 --- a/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll +++ b/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll @@ -100,7 +100,7 @@ module Stmts { // Note: If the brace statement is the body of a `TapExpr`, the first element is the variable // declaration (see https://github.com/apple/swift/blob/main/include/swift/AST/Expr.h#L848) - // that's initialized by the `Tapxpr`. In `TapExprTre` we've already visited this declaration, + // that's initialized by the `TapExpr`. In `TapExprTree` we've already visited this declaration, // along with its initializer. So we skip the first element here. private AstNode getFirstElement() { if this.isBodyOfTapExpr() then result = ast.getElement(1) else result = ast.getFirstElement() From e0dadb4df69da1449d68e337376e11655939c8c5 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 5 Aug 2022 10:20:07 +0200 Subject: [PATCH 451/505] Ruby: Simplify flow summaries for hash literals --- .../lib/codeql/ruby/frameworks/core/Hash.qll | 83 +++++++------------ 1 file changed, 31 insertions(+), 52 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/frameworks/core/Hash.qll b/ruby/ql/lib/codeql/ruby/frameworks/core/Hash.qll index 87733c0af9b..cf0872e770e 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/core/Hash.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/core/Hash.qll @@ -16,65 +16,44 @@ private import codeql.ruby.dataflow.internal.DataFlowDispatch * in `Array.qll`. */ module Hash { - // cannot use API graphs due to negative recursion - private predicate isHashLiteralPair(Pair pair, ConstantValue key) { - key = DataFlow::Content::getKnownElementIndex(pair.getKey()) and - pair = any(MethodCall mc | mc.getMethodName() = "[]").getAnArgument() - } - - private class HashLiteralSymbolSummary extends SummarizedCallable { - private ConstantValue::ConstantSymbolValue symbol; - - HashLiteralSymbolSummary() { - isHashLiteralPair(_, symbol) and - this = "Hash.[" + symbol.serialize() + "]" - } - - final override MethodCall getACall() { - result = API::getTopLevelMember("Hash").getAMethodCall("[]").getExprNode().getExpr() and - exists(result.getKeywordArgument(symbol.getSymbol())) - } - - override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { - // { symbol: x } - input = "Argument[" + symbol.getSymbol() + ":]" and - output = "ReturnValue.Element[" + symbol.serialize() + "]" and - preservesValue = true - } - } - - private class HashLiteralNonSymbolSummary extends SummarizedCallable { - private ConstantValue key; - - HashLiteralNonSymbolSummary() { - this = "Hash.[]" and - isHashLiteralPair(_, key) and + /** + * Holds if `key` is used as the non-symbol key in a hash literal. For example + * + * ```rb + * { + * :a => 1, # symbol + * "b" => 2 # non-symbol, "b" is the key + * } + * ``` + */ + private predicate isHashLiteralNonSymbolKey(ConstantValue key) { + exists(Pair pair | + key = DataFlow::Content::getKnownElementIndex(pair.getKey()) and + // cannot use API graphs due to negative recursion + pair = any(MethodCall mc | mc.getMethodName() = "[]").getAnArgument() and not key.isSymbol(_) - } + ) + } + + private class HashLiteralSummary extends SummarizedCallable { + HashLiteralSummary() { this = "Hash.[]" } final override MethodCall getACall() { - result = API::getTopLevelMember("Hash").getAMethodCall("[]").getExprNode().getExpr() and - isHashLiteralPair(result.getAnArgument(), key) + result = API::getTopLevelMember("Hash").getAMethodCall("[]").getExprNode().getExpr() } override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { // { 'nonsymbol' => x } - input = "Argument[0..].PairValue[" + key.serialize() + "]" and - output = "ReturnValue.Element[" + key.serialize() + "]" and - preservesValue = true - } - } - - private class HashLiteralHashSplatSummary extends SummarizedCallable { - HashLiteralHashSplatSummary() { this = "Hash.[**]" } - - final override MethodCall getACall() { - result = API::getTopLevelMember("Hash").getAMethodCall("[]").getExprNode().getExpr() and - result.getAnArgument() instanceof HashSplatExpr - } - - override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { - // { **hash } + exists(ConstantValue key | + isHashLiteralNonSymbolKey(key) and + input = "Argument[0..].PairValue[" + key.serialize() + "]" and + output = "ReturnValue.Element[" + key.serialize() + "]" and + preservesValue = true + ) + or + // { symbol: x } + // we make use of the special `hash-splat` argument kind, which contains all keyword + // arguments wrapped in an implicit hash, as well as explicit hash splat arguments input = "Argument[hash-splat].WithElement[any]" and output = "ReturnValue" and preservesValue = true From 5ebce6ee4f614c9b4c8e3e9cd870d4b2fbbba769 Mon Sep 17 00:00:00 2001 From: Tony Torralba Date: Fri, 5 Aug 2022 10:16:30 +0200 Subject: [PATCH 452/505] Improve AsyncTask data flow support Model the life-cycle described here: https://developer.android.com/reference/android/os/AsyncTask\#the-4-steps --- .../java/frameworks/android/AsyncTask.qll | 82 +++++++++++++++---- .../frameworks/android/asynctask/Test.java | 37 ++++++++- .../frameworks/android/asynctask/test.ql | 5 +- 3 files changed, 102 insertions(+), 22 deletions(-) diff --git a/java/ql/lib/semmle/code/java/frameworks/android/AsyncTask.qll b/java/ql/lib/semmle/code/java/frameworks/android/AsyncTask.qll index a4e7d11c549..0b0334a0664 100644 --- a/java/ql/lib/semmle/code/java/frameworks/android/AsyncTask.qll +++ b/java/ql/lib/semmle/code/java/frameworks/android/AsyncTask.qll @@ -4,19 +4,62 @@ import java private import semmle.code.java.dataflow.DataFlow private import semmle.code.java.dataflow.FlowSteps -/** - * Models the value-preserving step from `asyncTask.execute(params)` to `AsyncTask::doInBackground(params)`. +/* + * The following flow steps aim to model the life-cycle of `AsyncTask`s described here: + * https://developer.android.com/reference/android/os/AsyncTask#the-4-steps */ -private class AsyncTaskAdditionalValueStep extends AdditionalValueStep { + +/** + * A taint step from the vararg arguments of `AsyncTask::execute` and `AsyncTask::executeOnExecutor` + * to the parameter of `AsyncTask::doInBackground`. + */ +private class AsyncTaskExecuteAdditionalValueStep extends AdditionalTaintStep { override predicate step(DataFlow::Node node1, DataFlow::Node node2) { exists(ExecuteAsyncTaskMethodAccess ma, AsyncTaskRunInBackgroundMethod m | - DataFlow::getInstanceArgument(ma).getType() = m.getDeclaringType() and + DataFlow::getInstanceArgument(ma).getType() = m.getDeclaringType() + | node1.asExpr() = ma.getParamsArgument() and node2.asParameter() = m.getParameter(0) ) } } +/** + * A value-preserving step from the return value of `AsyncTask::doInBackground` + * to the parameter of `AsyncTask::onPostExecute`. + */ +private class AsyncTaskOnPostExecuteAdditionalValueStep extends AdditionalValueStep { + override predicate step(DataFlow::Node node1, DataFlow::Node node2) { + exists( + AsyncTaskRunInBackgroundMethod runInBackground, AsyncTaskOnPostExecuteMethod onPostExecute + | + onPostExecute.getDeclaringType() = runInBackground.getDeclaringType() + | + node1.asExpr() = any(ReturnStmt r | r.getEnclosingCallable() = runInBackground).getResult() and + node2.asParameter() = onPostExecute.getParameter(0) + ) + } +} + +/** + * A value-preserving step from field initializers in `AsyncTask`'s constructor or initializer method + * to the instance parameter of `AsyncTask::runInBackground` and `AsyncTask::onPostExecute`. + */ +private class AsyncTaskFieldInitQualifierToInstanceParameterStep extends AdditionalValueStep { + override predicate step(DataFlow::Node n1, DataFlow::Node n2) { + exists(AsyncTaskInit init, Callable receiver | + n1.(DataFlow::PostUpdateNode).getPreUpdateNode() = + DataFlow::getFieldQualifier(any(FieldWrite f | f.getEnclosingCallable() = init)) and + n2.(DataFlow::InstanceParameterNode).getCallable() = receiver and + receiver.getDeclaringType() = init.getDeclaringType() and + ( + receiver instanceof AsyncTaskRunInBackgroundMethod or + receiver instanceof AsyncTaskOnPostExecuteMethod + ) + ) + } +} + /** * The Android class `android.os.AsyncTask`. */ @@ -24,29 +67,40 @@ private class AsyncTask extends RefType { AsyncTask() { this.hasQualifiedName("android.os", "AsyncTask") } } +/** The constructor or initializer method of the `android.os.AsyncTask` class. */ +private class AsyncTaskInit extends Callable { + AsyncTaskInit() { + this.getDeclaringType().getSourceDeclaration().getASourceSupertype*() instanceof AsyncTask and + (this instanceof Constructor or this instanceof InitializerMethod) + } +} + /** A call to the `execute` or `executeOnExecutor` methods of the `android.os.AsyncTask` class. */ private class ExecuteAsyncTaskMethodAccess extends MethodAccess { Argument paramsArgument; ExecuteAsyncTaskMethodAccess() { - exists(Method m | - this.getMethod() = m and - m.getDeclaringType().getSourceDeclaration().getASourceSupertype*() instanceof AsyncTask - | - m.getName() = "execute" and not m.isStatic() and paramsArgument = this.getArgument(0) - or - m.getName() = "executeOnExecutor" and paramsArgument = this.getArgument(1) - ) + this.getMethod().hasName(["execute", "executeOnExecutor"]) and + this.getMethod().getDeclaringType().getSourceDeclaration().getASourceSupertype*() instanceof + AsyncTask } /** Returns the `params` argument of this call. */ - Argument getParamsArgument() { result = paramsArgument } + Argument getParamsArgument() { result = this.getAnArgument() and result.isVararg() } } /** The `doInBackground` method of the `android.os.AsyncTask` class. */ private class AsyncTaskRunInBackgroundMethod extends Method { AsyncTaskRunInBackgroundMethod() { this.getDeclaringType().getSourceDeclaration().getASourceSupertype*() instanceof AsyncTask and - this.getName() = "doInBackground" + this.hasName("doInBackground") + } +} + +/** The `onPostExecute` method of the `android.os.AsyncTask` class. */ +private class AsyncTaskOnPostExecuteMethod extends Method { + AsyncTaskOnPostExecuteMethod() { + this.getDeclaringType().getSourceDeclaration().getASourceSupertype*() instanceof AsyncTask and + this.hasName("onPostExecute") } } diff --git a/java/ql/test/library-tests/frameworks/android/asynctask/Test.java b/java/ql/test/library-tests/frameworks/android/asynctask/Test.java index a2c25487462..b471b172c64 100644 --- a/java/ql/test/library-tests/frameworks/android/asynctask/Test.java +++ b/java/ql/test/library-tests/frameworks/android/asynctask/Test.java @@ -10,16 +10,19 @@ public class Test { public void test() { TestAsyncTask t = new TestAsyncTask(); - t.execute(source("execute")); - t.executeOnExecutor(null, source("executeOnExecutor")); + t.execute(source("execute"), null); + t.executeOnExecutor(null, source("executeOnExecutor"), null); SafeAsyncTask t2 = new SafeAsyncTask(); t2.execute("safe"); + TestConstructorTask t3 = new TestConstructorTask(source("constructor"), "safe"); + t3.execute(source("params")); } private class TestAsyncTask extends AsyncTask { @Override protected Object doInBackground(Object... params) { - sink(params); // $ hasValueFlow=execute hasValueFlow=executeOnExecutor + sink(params[0]); // $ hasTaintFlow=execute hasTaintFlow=executeOnExecutor + sink(params[1]); // $ SPURIOUS: hasTaintFlow=execute hasTaintFlow=executeOnExecutor return null; } } @@ -27,8 +30,34 @@ public class Test { private class SafeAsyncTask extends AsyncTask { @Override protected Object doInBackground(Object... params) { - sink(params); // Safe + sink(params[0]); // Safe return null; } } + + class TestConstructorTask extends AsyncTask { + private Object field; + private Object safeField; + + public TestConstructorTask(Object field, Object safeField) { + this.field = field; + this.safeField = safeField; + } + + @Override + protected Object doInBackground(Object... params) { + sink(params[0]); // $ hasTaintFlow=params + sink(field); // $ hasValueFlow=constructor + sink(safeField); // Safe + return params[0]; + } + + @Override + protected void onPostExecute(Object param) { + sink(param); // $ hasTaintFlow=params + sink(field); // $ hasValueFlow=constructor + sink(safeField); // Safe + } + + } } diff --git a/java/ql/test/library-tests/frameworks/android/asynctask/test.ql b/java/ql/test/library-tests/frameworks/android/asynctask/test.ql index 9c9b3747604..58d90963fcb 100644 --- a/java/ql/test/library-tests/frameworks/android/asynctask/test.ql +++ b/java/ql/test/library-tests/frameworks/android/asynctask/test.ql @@ -1,6 +1,3 @@ import java import TestUtilities.InlineFlowTest - -class AsyncTaskTest extends InlineFlowTest { - override TaintTracking::Configuration getTaintFlowConfig() { none() } -} +import semmle.code.java.dataflow.FlowSteps From 09d0f8e0cee79406d4a49df232ca10d297c57d12 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Wed, 22 Jun 2022 13:49:10 +0200 Subject: [PATCH 453/505] Dataflow: Replace stage duplication with parameterised modules. --- .../java/dataflow/internal/DataFlowImpl.qll | 2507 +++++------------ 1 file changed, 766 insertions(+), 1741 deletions(-) diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll index a076f7a2e45..22c3bd2466e 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,12 +944,9 @@ private module Stage1 { predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) } bindingset[node, state, config] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, toReturn, pragma[only_bind_into](config)) and + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, _, pragma[only_bind_into](config)) and exists(state) and - exists(returnAp) and exists(ap) } @@ -1142,66 +1139,754 @@ private predicate flowIntoCallNodeCand1( ) } -private module Stage2 { - module PrevStage = Stage1; +private signature module StageSig { + class Ap; + predicate revFlow(NodeEx node, Configuration config); + + bindingset[node, state, config] + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config); + + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ); + + predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config); +} + +private module MkStage { class ApApprox = PrevStage::Ap; - class Ap = boolean; + signature module StageParam { + class Ap; - class ApNil extends Ap { - ApNil() { this = false } + class ApNil extends Ap; + + bindingset[result, ap] + ApApprox getApprox(Ap ap); + + ApNil getApNil(NodeEx node); + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail); + + Content getHeadContent(Ap ap); + + class ApOption; + + ApOption apNone(); + + ApOption apSome(Ap ap); + + class Cc; + + class CcCall extends Cc; + + // TODO: member predicate on CcCall + predicate matchesCall(CcCall cc, DataFlowCall call); + + class CcNoCall extends Cc; + + Cc ccNone(); + + CcCall ccSomeCall(); + + class LocalCc; + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc); + + bindingset[node1, state1, config] + bindingset[node2, state2, config] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ); + + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + ); + + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ); + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config); + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType); } - bindingset[result, ap] - private ApApprox getApprox(Ap ap) { any() } + module Stage implements StageSig { + import Param - private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) } + /* Begin: Stage logic. */ + bindingset[result, apa] + private ApApprox unbindApa(ApApprox apa) { + pragma[only_bind_out](apa) = pragma[only_bind_out](result) + } - bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, + Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, + pragma[only_bind_into](config)) and + matchesCall(ccc, call) + } - pragma[inline] - private Content getHeadContent(Ap ap) { exists(result) and ap = true } + /** + * Holds if `node` is reachable with access path `ap` from a source in the + * configuration `config`. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. + */ + pragma[nomagic] + predicate fwdFlow( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + fwdFlow0(node, state, cc, argAp, ap, config) and + PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and + filter(node, state, ap, config) + } - class ApOption = BooleanOption; + pragma[nomagic] + private predicate fwdFlow0( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + sourceNode(node, state, config) and + (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and + argAp = apNone() and + ap = getApNil(node) + or + exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | + fwdFlow(mid, state0, cc, argAp, ap0, config) and + localCc = getLocalCc(mid, cc) + | + localStep(mid, state0, node, state, true, _, config, localCc) and + ap = ap0 + or + localStep(mid, state0, node, state, false, ap, config, localCc) and + ap0 instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + jumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStateStep(mid, state0, node, state, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + // store + exists(TypedContent tc, Ap ap0 | + fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + ap = apCons(tc, ap0) + ) + or + // read + exists(Ap ap0, Content c | + fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowConsCand(ap0, c, ap, config) + ) + or + // flow into a callable + exists(ApApprox apa | + fwdFlowIn(_, node, state, _, cc, _, ap, config) and + apa = getApprox(ap) and + if PrevStage::parameterMayFlowThrough(node, _, apa, config) + then argAp = apSome(ap) + else argAp = apNone() + ) + or + // flow out of a callable + fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) + ) + } - ApOption apNone() { result = TBooleanNone() } + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + exists(DataFlowType contentType | + fwdFlow(node1, state, cc, argAp, ap1, config) and + PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and + typecheckStore(ap1, contentType) + ) + } - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + /** + * Holds if forward flow with access path `tail` reaches a store of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(TypedContent tc | + fwdFlowStore(_, tail, tc, _, _, _, _, config) and + tc.getContent() = c and + cons = apCons(tc, tail) + ) + } + pragma[nomagic] + private predicate fwdFlowRead( + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + fwdFlow(node1, state, cc, argAp, ap, config) and + PrevStage::readStepCand(node1, c, node2, config) and + getHeadContent(ap) = c + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, + Ap ap, Configuration config + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, state, outercc, argAp, ap, config) and + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutNotFromArg( + NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + ) { + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, argAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + ccOut = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + ) + } + + pragma[nomagic] + private predicate storeStepFwd( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config + ) { + fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + ap2 = apCons(tc, ap1) and + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + } + + private predicate readStepFwd( + NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config + ) { + fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowConsCand(ap1, c, ap2, config) + } + + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and + fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), + pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + + pragma[nomagic] + private predicate returnNodeMayFlowThrough( + RetNodeEx ret, FlowState state, Ap ap, Configuration config + ) { + fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from the + * enclosing callable in order to reach a sink, and if so, `returnAp` records + * the access path of the returned value. + */ + pragma[nomagic] + predicate revFlow( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + revFlow0(node, state, toReturn, returnAp, ap, config) and + fwdFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + fwdFlow(node, state, _, _, ap, config) and + sinkNode(node, state, config) and + (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid, FlowState state0 | + localStep(node, state, mid, state0, true, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, ap, config) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, state, _, _, ap, config) and + toReturn = false and + returnAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStep(node, mid, config) and + revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStateStep(node, state, mid, state0, config) and + revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, + pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowConsCand(ap0, c, ap, config) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, state, toReturn, returnAp, ap0, config) and + readStepFwd(node, ap, _, mid, ap0, config) + ) + or + // flow into a callable + revFlowInNotToReturn(node, state, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + or + // flow out of a callable + revFlowOut(_, node, state, _, _, ap, config) and + toReturn = true and + if returnNodeMayFlowThrough(node, state, ap, config) + then returnAp = apSome(ap) + else returnAp = apNone() + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, + boolean toReturn, ApOption returnAp, Configuration config + ) { + revFlow(mid, state, toReturn, returnAp, ap0, config) and + storeStepFwd(node, ap, tc, mid, ap0, config) and + tc.getContent() = c + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, _, tail, config) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0, config) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, + Configuration config + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, state, toReturn, returnAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInNotToReturn( + ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, FlowState state, CcCall ccc | + revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and + fwdFlow(ret, state, ccc, apSome(_), ap, config) and + matchesCall(ccc, call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Ap ap2, Content c | + PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and + revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and + revFlowConsCand(ap2, c, ap1, config) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and + readStepFwd(node1, ap1, c, node2, ap2, config) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, + pragma[only_bind_into](config)) + ) + } + + predicate revFlow(NodeEx node, FlowState state, Configuration config) { + revFlow(node, state, _, _, _, config) + } + + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, ap, config) + } + + private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepFwd(_, ap, tc, _, _, config) + } + + private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepCand(_, ap, tc, _, _, config) + } + + private predicate validAp(Ap ap, Configuration config) { + revFlow(_, _, _, _, ap, config) and ap instanceof ApNil + or + exists(TypedContent head, Ap tail | + consCand(head, tail, config) and + ap = apCons(head, tail) + ) + } + + predicate consCand(TypedContent tc, Ap ap, Configuration config) { + revConsCand(tc, ap, config) and + validAp(ap, config) + } + + pragma[noinline] + private predicate parameterFlow( + ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ) { + revFlow(p, _, true, apSome(ap0), ap, config) and + c = p.getEnclosingCallable() + } + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | + parameterFlow(p, ap, ap0, c, config) and + c = ret.getEnclosingCallable() and + revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), + pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and + fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and + kind = ret.getKind() and + p.getPosition() = pos and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists( + Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + | + revFlow(arg, state, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + + predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, argAp, ap, config) + ) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | consCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and + states = count(FlowState state | revFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | + revFlow(n, state, b, retAp, ap, config) + ) + } + /* End: Stage logic. */ + } +} + +private module BooleanCallContext { + class Cc extends boolean { + Cc() { this in [true, false] } + } + + class CcCall extends Cc { + CcCall() { this = true } + } + + /** Holds if the call context may be `call`. */ + predicate matchesCall(CcCall cc, DataFlowCall call) { any() } + + class CcNoCall extends Cc { + CcNoCall() { this = false } + } + + Cc ccNone() { result = false } + + CcCall ccSomeCall() { result = true } + + class LocalCc = Unit; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } +} + +private module Level1CallContext { class Cc = CallContext; class CcCall = CallContextCall; + pragma[inline] + predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } + class CcNoCall = CallContextNoCall; Cc ccNone() { result instanceof CallContextAny } CcCall ccSomeCall() { result instanceof CallContextSomeCall } - private class LocalCc = Unit; + module NoLocalCallContext { + class LocalCc = Unit; - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSiteDispatch(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSiteDispatch(call, c) + then result = TSpecificCall(call) + else result = TSomeCall() + } + } + + module LocalCallContext { + class LocalCc = LocalCallContext; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { + result = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + node.getEnclosingCallable()) + } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() + } } bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { checkCallContextReturn(innercc, c, call) and if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() } +} - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } +private module Stage2Param implements MkStage::StageParam { + private module PrevStage = Stage1; + + class Ap extends boolean { + Ap() { this in [true, false] } + } + + class ApNil extends Ap { + ApNil() { this = false } + } + + bindingset[result, ap] + PrevStage::Ap getApprox(Ap ap) { any() } + + ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + + pragma[inline] + Content getHeadContent(Ap ap) { exists(result) and ap = true } + + class ApOption = BooleanOption; + + ApOption apNone() { result = TBooleanNone() } + + ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + + import Level1CallContext + import NoLocalCallContext bindingset[node1, state1, config] bindingset[node2, state2, config] - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -1221,9 +1906,9 @@ private module Stage2 { exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/5; - private predicate flowIntoCall = flowIntoCallNodeCand1/5; + predicate flowIntoCall = flowIntoCallNodeCand1/5; pragma[nomagic] private predicate expectsContentCand(NodeEx node, Configuration config) { @@ -1235,7 +1920,7 @@ private module Stage2 { } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and not stateBarrier(node, state, config) and @@ -1248,544 +1933,11 @@ private module Stage2 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 2 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 2 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage2 = MkStage::Stage; + pragma[nomagic] private predicate flowOutOfCallNodeCand2( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config @@ -1883,14 +2035,13 @@ private module LocalFlowBigStep { ) { additionalLocalFlowStepNodeCand1(node1, node2, config) and state1 = state2 and - Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false, - pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false, + Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false, pragma[only_bind_into](config)) or additionalLocalStateStep(node1, state1, node2, state2, config) and - Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config)) + Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config)) } /** @@ -1967,26 +2118,24 @@ private module LocalFlowBigStep { private import LocalFlowBigStep -private module Stage3 { - module PrevStage = Stage2; - - class ApApprox = PrevStage::Ap; +private module Stage3Param implements MkStage::StageParam { + private module PrevStage = Stage2; class Ap = AccessPathFront; class ApNil = AccessPathFrontNil; - private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } + PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } + Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathFrontOption; @@ -1994,44 +2143,18 @@ private module Stage3 { ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - class Cc = boolean; + import BooleanCallContext - class CcCall extends Cc { - CcCall() { this = true } - - /** Holds if this call context may be `call`. */ - predicate matchesCall(DataFlowCall call) { any() } - } - - class CcNoCall extends Cc { - CcNoCall() { this = false } - } - - Cc ccNone() { result = false } - - CcCall ccSomeCall() { result = true } - - private class LocalCc = Unit; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/5; - private predicate flowIntoCall = flowIntoCallNodeCand2/5; + predicate flowIntoCall = flowIntoCallNodeCand2/5; pragma[nomagic] private predicate clearSet(NodeEx node, ContentSet c, Configuration config) { @@ -2067,7 +2190,7 @@ private module Stage3 { private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { exists(state) and exists(config) and not clear(node, ap, config) and @@ -2080,548 +2203,15 @@ private module Stage3 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { + predicate typecheckStore(Ap ap, DataFlowType contentType) { // We need to typecheck stores here, since reverse flow through a getter // might have a different type here compared to inside the getter. compatibleTypes(ap.getType(), contentType) } - - /* Begin: Stage 3 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 3 logic. */ } +private module Stage3 = MkStage::Stage; + /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` * and remains relevant for the following pruning stage. @@ -2644,7 +2234,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and nodes = strictcount(NodeEx n, FlowState state | - Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) or flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) ) and @@ -2828,26 +2418,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } } -private module Stage4 { - module PrevStage = Stage3; - - class ApApprox = PrevStage::Ap; +private module Stage4Param implements MkStage::StageParam { + private module PrevStage = Stage3; class Ap = AccessPathApprox; class ApNil = AccessPathApproxNil; - private ApApprox getApprox(Ap ap) { result = ap.getFront() } + PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } + Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathApproxOption; @@ -2855,38 +2443,10 @@ private module Stage4 { ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - class Cc = CallContext; + import Level1CallContext + import LocalCallContext - class CcCall = CallContextCall; - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - private class LocalCc = LocalCallContext; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() - } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { - result = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - node.getEnclosingCallable()) - } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -2894,575 +2454,40 @@ private module Stage4 { } pragma[nomagic] - private predicate flowOutOfCall( + predicate flowOutOfCall( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } pragma[nomagic] - private predicate flowIntoCall( + predicate flowIntoCall( DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } // Type checking is not necessary here as it has already been done in stage 3. bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 4 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 4 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage4 = MkStage::Stage; + bindingset[conf, result] private Configuration unbindConf(Configuration conf) { exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) @@ -3495,7 +2520,7 @@ private newtype TSummaryCtx = TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and - Stage4::revFlow(p, state, _, _, _, config) + Stage4::revFlow(p, state, _, config) ) } @@ -3553,7 +2578,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { result = strictcount(NodeEx n, FlowState state | - Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config) + Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config) ) } @@ -3667,7 +2692,7 @@ private newtype TPathNode = exists(PathNodeMid mid | pathStep(mid, node, state, cc, sc, ap) and pragma[only_bind_into](config) = mid.getConfiguration() and - Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config)) + Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config)) ) } or TPathNodeSink(NodeEx node, FlowState state, Configuration config) { @@ -4207,7 +3232,7 @@ private NodeEx getAnOutNodeFlow( ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result.asNode() = kind.getAnOutNode(call) and - Stage4::revFlow(result, _, _, _, apa, config) + Stage4::revFlow(result, _, apa, config) } /** @@ -4243,7 +3268,7 @@ private predicate parameterCand( DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config ) { exists(ParamNodeEx p | - Stage4::revFlow(p, _, _, _, apa, config) and + Stage4::revFlow(p, _, apa, config) and p.isParameterOf(callable, pos) ) } From d3dcc3ce3a9db8a96d62096f6209d675cbbf72e1 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Thu, 14 Jul 2022 11:52:13 +0200 Subject: [PATCH 454/505] Dataflow: Sync. --- .../cpp/dataflow/internal/DataFlowImpl.qll | 2507 +++++------------ .../cpp/dataflow/internal/DataFlowImpl2.qll | 2507 +++++------------ .../cpp/dataflow/internal/DataFlowImpl3.qll | 2507 +++++------------ .../cpp/dataflow/internal/DataFlowImpl4.qll | 2507 +++++------------ .../dataflow/internal/DataFlowImplLocal.qll | 2507 +++++------------ .../cpp/ir/dataflow/internal/DataFlowImpl.qll | 2507 +++++------------ .../ir/dataflow/internal/DataFlowImpl2.qll | 2507 +++++------------ .../ir/dataflow/internal/DataFlowImpl3.qll | 2507 +++++------------ .../ir/dataflow/internal/DataFlowImpl4.qll | 2507 +++++------------ .../csharp/dataflow/internal/DataFlowImpl.qll | 2507 +++++------------ .../dataflow/internal/DataFlowImpl2.qll | 2507 +++++------------ .../dataflow/internal/DataFlowImpl3.qll | 2507 +++++------------ .../dataflow/internal/DataFlowImpl4.qll | 2507 +++++------------ .../dataflow/internal/DataFlowImpl5.qll | 2507 +++++------------ .../DataFlowImplForContentDataFlow.qll | 2507 +++++------------ .../java/dataflow/internal/DataFlowImpl2.qll | 2507 +++++------------ .../java/dataflow/internal/DataFlowImpl3.qll | 2507 +++++------------ .../java/dataflow/internal/DataFlowImpl4.qll | 2507 +++++------------ .../java/dataflow/internal/DataFlowImpl5.qll | 2507 +++++------------ .../java/dataflow/internal/DataFlowImpl6.qll | 2507 +++++------------ .../DataFlowImplForOnActivityResult.qll | 2507 +++++------------ .../DataFlowImplForSerializability.qll | 2507 +++++------------ .../dataflow/new/internal/DataFlowImpl.qll | 2507 +++++------------ .../dataflow/new/internal/DataFlowImpl2.qll | 2507 +++++------------ .../dataflow/new/internal/DataFlowImpl3.qll | 2507 +++++------------ .../dataflow/new/internal/DataFlowImpl4.qll | 2507 +++++------------ .../ruby/dataflow/internal/DataFlowImpl.qll | 2507 +++++------------ .../ruby/dataflow/internal/DataFlowImpl2.qll | 2507 +++++------------ .../internal/DataFlowImplForLibraries.qll | 2507 +++++------------ .../swift/dataflow/internal/DataFlowImpl.qll | 2507 +++++------------ 30 files changed, 22980 insertions(+), 52230 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll index a076f7a2e45..22c3bd2466e 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,12 +944,9 @@ private module Stage1 { predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) } bindingset[node, state, config] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, toReturn, pragma[only_bind_into](config)) and + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, _, pragma[only_bind_into](config)) and exists(state) and - exists(returnAp) and exists(ap) } @@ -1142,66 +1139,754 @@ private predicate flowIntoCallNodeCand1( ) } -private module Stage2 { - module PrevStage = Stage1; +private signature module StageSig { + class Ap; + predicate revFlow(NodeEx node, Configuration config); + + bindingset[node, state, config] + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config); + + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ); + + predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config); +} + +private module MkStage { class ApApprox = PrevStage::Ap; - class Ap = boolean; + signature module StageParam { + class Ap; - class ApNil extends Ap { - ApNil() { this = false } + class ApNil extends Ap; + + bindingset[result, ap] + ApApprox getApprox(Ap ap); + + ApNil getApNil(NodeEx node); + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail); + + Content getHeadContent(Ap ap); + + class ApOption; + + ApOption apNone(); + + ApOption apSome(Ap ap); + + class Cc; + + class CcCall extends Cc; + + // TODO: member predicate on CcCall + predicate matchesCall(CcCall cc, DataFlowCall call); + + class CcNoCall extends Cc; + + Cc ccNone(); + + CcCall ccSomeCall(); + + class LocalCc; + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc); + + bindingset[node1, state1, config] + bindingset[node2, state2, config] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ); + + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + ); + + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ); + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config); + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType); } - bindingset[result, ap] - private ApApprox getApprox(Ap ap) { any() } + module Stage implements StageSig { + import Param - private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) } + /* Begin: Stage logic. */ + bindingset[result, apa] + private ApApprox unbindApa(ApApprox apa) { + pragma[only_bind_out](apa) = pragma[only_bind_out](result) + } - bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, + Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, + pragma[only_bind_into](config)) and + matchesCall(ccc, call) + } - pragma[inline] - private Content getHeadContent(Ap ap) { exists(result) and ap = true } + /** + * Holds if `node` is reachable with access path `ap` from a source in the + * configuration `config`. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. + */ + pragma[nomagic] + predicate fwdFlow( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + fwdFlow0(node, state, cc, argAp, ap, config) and + PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and + filter(node, state, ap, config) + } - class ApOption = BooleanOption; + pragma[nomagic] + private predicate fwdFlow0( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + sourceNode(node, state, config) and + (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and + argAp = apNone() and + ap = getApNil(node) + or + exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | + fwdFlow(mid, state0, cc, argAp, ap0, config) and + localCc = getLocalCc(mid, cc) + | + localStep(mid, state0, node, state, true, _, config, localCc) and + ap = ap0 + or + localStep(mid, state0, node, state, false, ap, config, localCc) and + ap0 instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + jumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStateStep(mid, state0, node, state, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + // store + exists(TypedContent tc, Ap ap0 | + fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + ap = apCons(tc, ap0) + ) + or + // read + exists(Ap ap0, Content c | + fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowConsCand(ap0, c, ap, config) + ) + or + // flow into a callable + exists(ApApprox apa | + fwdFlowIn(_, node, state, _, cc, _, ap, config) and + apa = getApprox(ap) and + if PrevStage::parameterMayFlowThrough(node, _, apa, config) + then argAp = apSome(ap) + else argAp = apNone() + ) + or + // flow out of a callable + fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) + ) + } - ApOption apNone() { result = TBooleanNone() } + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + exists(DataFlowType contentType | + fwdFlow(node1, state, cc, argAp, ap1, config) and + PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and + typecheckStore(ap1, contentType) + ) + } - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + /** + * Holds if forward flow with access path `tail` reaches a store of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(TypedContent tc | + fwdFlowStore(_, tail, tc, _, _, _, _, config) and + tc.getContent() = c and + cons = apCons(tc, tail) + ) + } + pragma[nomagic] + private predicate fwdFlowRead( + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + fwdFlow(node1, state, cc, argAp, ap, config) and + PrevStage::readStepCand(node1, c, node2, config) and + getHeadContent(ap) = c + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, + Ap ap, Configuration config + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, state, outercc, argAp, ap, config) and + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutNotFromArg( + NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + ) { + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, argAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + ccOut = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + ) + } + + pragma[nomagic] + private predicate storeStepFwd( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config + ) { + fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + ap2 = apCons(tc, ap1) and + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + } + + private predicate readStepFwd( + NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config + ) { + fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowConsCand(ap1, c, ap2, config) + } + + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and + fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), + pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + + pragma[nomagic] + private predicate returnNodeMayFlowThrough( + RetNodeEx ret, FlowState state, Ap ap, Configuration config + ) { + fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from the + * enclosing callable in order to reach a sink, and if so, `returnAp` records + * the access path of the returned value. + */ + pragma[nomagic] + predicate revFlow( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + revFlow0(node, state, toReturn, returnAp, ap, config) and + fwdFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + fwdFlow(node, state, _, _, ap, config) and + sinkNode(node, state, config) and + (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid, FlowState state0 | + localStep(node, state, mid, state0, true, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, ap, config) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, state, _, _, ap, config) and + toReturn = false and + returnAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStep(node, mid, config) and + revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStateStep(node, state, mid, state0, config) and + revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, + pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowConsCand(ap0, c, ap, config) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, state, toReturn, returnAp, ap0, config) and + readStepFwd(node, ap, _, mid, ap0, config) + ) + or + // flow into a callable + revFlowInNotToReturn(node, state, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + or + // flow out of a callable + revFlowOut(_, node, state, _, _, ap, config) and + toReturn = true and + if returnNodeMayFlowThrough(node, state, ap, config) + then returnAp = apSome(ap) + else returnAp = apNone() + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, + boolean toReturn, ApOption returnAp, Configuration config + ) { + revFlow(mid, state, toReturn, returnAp, ap0, config) and + storeStepFwd(node, ap, tc, mid, ap0, config) and + tc.getContent() = c + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, _, tail, config) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0, config) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, + Configuration config + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, state, toReturn, returnAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInNotToReturn( + ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, FlowState state, CcCall ccc | + revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and + fwdFlow(ret, state, ccc, apSome(_), ap, config) and + matchesCall(ccc, call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Ap ap2, Content c | + PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and + revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and + revFlowConsCand(ap2, c, ap1, config) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and + readStepFwd(node1, ap1, c, node2, ap2, config) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, + pragma[only_bind_into](config)) + ) + } + + predicate revFlow(NodeEx node, FlowState state, Configuration config) { + revFlow(node, state, _, _, _, config) + } + + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, ap, config) + } + + private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepFwd(_, ap, tc, _, _, config) + } + + private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepCand(_, ap, tc, _, _, config) + } + + private predicate validAp(Ap ap, Configuration config) { + revFlow(_, _, _, _, ap, config) and ap instanceof ApNil + or + exists(TypedContent head, Ap tail | + consCand(head, tail, config) and + ap = apCons(head, tail) + ) + } + + predicate consCand(TypedContent tc, Ap ap, Configuration config) { + revConsCand(tc, ap, config) and + validAp(ap, config) + } + + pragma[noinline] + private predicate parameterFlow( + ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ) { + revFlow(p, _, true, apSome(ap0), ap, config) and + c = p.getEnclosingCallable() + } + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | + parameterFlow(p, ap, ap0, c, config) and + c = ret.getEnclosingCallable() and + revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), + pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and + fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and + kind = ret.getKind() and + p.getPosition() = pos and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists( + Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + | + revFlow(arg, state, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + + predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, argAp, ap, config) + ) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | consCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and + states = count(FlowState state | revFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | + revFlow(n, state, b, retAp, ap, config) + ) + } + /* End: Stage logic. */ + } +} + +private module BooleanCallContext { + class Cc extends boolean { + Cc() { this in [true, false] } + } + + class CcCall extends Cc { + CcCall() { this = true } + } + + /** Holds if the call context may be `call`. */ + predicate matchesCall(CcCall cc, DataFlowCall call) { any() } + + class CcNoCall extends Cc { + CcNoCall() { this = false } + } + + Cc ccNone() { result = false } + + CcCall ccSomeCall() { result = true } + + class LocalCc = Unit; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } +} + +private module Level1CallContext { class Cc = CallContext; class CcCall = CallContextCall; + pragma[inline] + predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } + class CcNoCall = CallContextNoCall; Cc ccNone() { result instanceof CallContextAny } CcCall ccSomeCall() { result instanceof CallContextSomeCall } - private class LocalCc = Unit; + module NoLocalCallContext { + class LocalCc = Unit; - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSiteDispatch(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSiteDispatch(call, c) + then result = TSpecificCall(call) + else result = TSomeCall() + } + } + + module LocalCallContext { + class LocalCc = LocalCallContext; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { + result = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + node.getEnclosingCallable()) + } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() + } } bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { checkCallContextReturn(innercc, c, call) and if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() } +} - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } +private module Stage2Param implements MkStage::StageParam { + private module PrevStage = Stage1; + + class Ap extends boolean { + Ap() { this in [true, false] } + } + + class ApNil extends Ap { + ApNil() { this = false } + } + + bindingset[result, ap] + PrevStage::Ap getApprox(Ap ap) { any() } + + ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + + pragma[inline] + Content getHeadContent(Ap ap) { exists(result) and ap = true } + + class ApOption = BooleanOption; + + ApOption apNone() { result = TBooleanNone() } + + ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + + import Level1CallContext + import NoLocalCallContext bindingset[node1, state1, config] bindingset[node2, state2, config] - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -1221,9 +1906,9 @@ private module Stage2 { exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/5; - private predicate flowIntoCall = flowIntoCallNodeCand1/5; + predicate flowIntoCall = flowIntoCallNodeCand1/5; pragma[nomagic] private predicate expectsContentCand(NodeEx node, Configuration config) { @@ -1235,7 +1920,7 @@ private module Stage2 { } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and not stateBarrier(node, state, config) and @@ -1248,544 +1933,11 @@ private module Stage2 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 2 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 2 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage2 = MkStage::Stage; + pragma[nomagic] private predicate flowOutOfCallNodeCand2( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config @@ -1883,14 +2035,13 @@ private module LocalFlowBigStep { ) { additionalLocalFlowStepNodeCand1(node1, node2, config) and state1 = state2 and - Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false, - pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false, + Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false, pragma[only_bind_into](config)) or additionalLocalStateStep(node1, state1, node2, state2, config) and - Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config)) + Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config)) } /** @@ -1967,26 +2118,24 @@ private module LocalFlowBigStep { private import LocalFlowBigStep -private module Stage3 { - module PrevStage = Stage2; - - class ApApprox = PrevStage::Ap; +private module Stage3Param implements MkStage::StageParam { + private module PrevStage = Stage2; class Ap = AccessPathFront; class ApNil = AccessPathFrontNil; - private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } + PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } + Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathFrontOption; @@ -1994,44 +2143,18 @@ private module Stage3 { ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - class Cc = boolean; + import BooleanCallContext - class CcCall extends Cc { - CcCall() { this = true } - - /** Holds if this call context may be `call`. */ - predicate matchesCall(DataFlowCall call) { any() } - } - - class CcNoCall extends Cc { - CcNoCall() { this = false } - } - - Cc ccNone() { result = false } - - CcCall ccSomeCall() { result = true } - - private class LocalCc = Unit; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/5; - private predicate flowIntoCall = flowIntoCallNodeCand2/5; + predicate flowIntoCall = flowIntoCallNodeCand2/5; pragma[nomagic] private predicate clearSet(NodeEx node, ContentSet c, Configuration config) { @@ -2067,7 +2190,7 @@ private module Stage3 { private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { exists(state) and exists(config) and not clear(node, ap, config) and @@ -2080,548 +2203,15 @@ private module Stage3 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { + predicate typecheckStore(Ap ap, DataFlowType contentType) { // We need to typecheck stores here, since reverse flow through a getter // might have a different type here compared to inside the getter. compatibleTypes(ap.getType(), contentType) } - - /* Begin: Stage 3 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 3 logic. */ } +private module Stage3 = MkStage::Stage; + /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` * and remains relevant for the following pruning stage. @@ -2644,7 +2234,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and nodes = strictcount(NodeEx n, FlowState state | - Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) or flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) ) and @@ -2828,26 +2418,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } } -private module Stage4 { - module PrevStage = Stage3; - - class ApApprox = PrevStage::Ap; +private module Stage4Param implements MkStage::StageParam { + private module PrevStage = Stage3; class Ap = AccessPathApprox; class ApNil = AccessPathApproxNil; - private ApApprox getApprox(Ap ap) { result = ap.getFront() } + PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } + Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathApproxOption; @@ -2855,38 +2443,10 @@ private module Stage4 { ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - class Cc = CallContext; + import Level1CallContext + import LocalCallContext - class CcCall = CallContextCall; - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - private class LocalCc = LocalCallContext; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() - } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { - result = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - node.getEnclosingCallable()) - } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -2894,575 +2454,40 @@ private module Stage4 { } pragma[nomagic] - private predicate flowOutOfCall( + predicate flowOutOfCall( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } pragma[nomagic] - private predicate flowIntoCall( + predicate flowIntoCall( DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } // Type checking is not necessary here as it has already been done in stage 3. bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 4 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 4 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage4 = MkStage::Stage; + bindingset[conf, result] private Configuration unbindConf(Configuration conf) { exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) @@ -3495,7 +2520,7 @@ private newtype TSummaryCtx = TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and - Stage4::revFlow(p, state, _, _, _, config) + Stage4::revFlow(p, state, _, config) ) } @@ -3553,7 +2578,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { result = strictcount(NodeEx n, FlowState state | - Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config) + Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config) ) } @@ -3667,7 +2692,7 @@ private newtype TPathNode = exists(PathNodeMid mid | pathStep(mid, node, state, cc, sc, ap) and pragma[only_bind_into](config) = mid.getConfiguration() and - Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config)) + Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config)) ) } or TPathNodeSink(NodeEx node, FlowState state, Configuration config) { @@ -4207,7 +3232,7 @@ private NodeEx getAnOutNodeFlow( ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result.asNode() = kind.getAnOutNode(call) and - Stage4::revFlow(result, _, _, _, apa, config) + Stage4::revFlow(result, _, apa, config) } /** @@ -4243,7 +3268,7 @@ private predicate parameterCand( DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config ) { exists(ParamNodeEx p | - Stage4::revFlow(p, _, _, _, apa, config) and + Stage4::revFlow(p, _, apa, config) and p.isParameterOf(callable, pos) ) } diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll index a076f7a2e45..22c3bd2466e 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,12 +944,9 @@ private module Stage1 { predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) } bindingset[node, state, config] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, toReturn, pragma[only_bind_into](config)) and + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, _, pragma[only_bind_into](config)) and exists(state) and - exists(returnAp) and exists(ap) } @@ -1142,66 +1139,754 @@ private predicate flowIntoCallNodeCand1( ) } -private module Stage2 { - module PrevStage = Stage1; +private signature module StageSig { + class Ap; + predicate revFlow(NodeEx node, Configuration config); + + bindingset[node, state, config] + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config); + + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ); + + predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config); +} + +private module MkStage { class ApApprox = PrevStage::Ap; - class Ap = boolean; + signature module StageParam { + class Ap; - class ApNil extends Ap { - ApNil() { this = false } + class ApNil extends Ap; + + bindingset[result, ap] + ApApprox getApprox(Ap ap); + + ApNil getApNil(NodeEx node); + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail); + + Content getHeadContent(Ap ap); + + class ApOption; + + ApOption apNone(); + + ApOption apSome(Ap ap); + + class Cc; + + class CcCall extends Cc; + + // TODO: member predicate on CcCall + predicate matchesCall(CcCall cc, DataFlowCall call); + + class CcNoCall extends Cc; + + Cc ccNone(); + + CcCall ccSomeCall(); + + class LocalCc; + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc); + + bindingset[node1, state1, config] + bindingset[node2, state2, config] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ); + + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + ); + + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ); + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config); + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType); } - bindingset[result, ap] - private ApApprox getApprox(Ap ap) { any() } + module Stage implements StageSig { + import Param - private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) } + /* Begin: Stage logic. */ + bindingset[result, apa] + private ApApprox unbindApa(ApApprox apa) { + pragma[only_bind_out](apa) = pragma[only_bind_out](result) + } - bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, + Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, + pragma[only_bind_into](config)) and + matchesCall(ccc, call) + } - pragma[inline] - private Content getHeadContent(Ap ap) { exists(result) and ap = true } + /** + * Holds if `node` is reachable with access path `ap` from a source in the + * configuration `config`. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. + */ + pragma[nomagic] + predicate fwdFlow( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + fwdFlow0(node, state, cc, argAp, ap, config) and + PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and + filter(node, state, ap, config) + } - class ApOption = BooleanOption; + pragma[nomagic] + private predicate fwdFlow0( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + sourceNode(node, state, config) and + (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and + argAp = apNone() and + ap = getApNil(node) + or + exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | + fwdFlow(mid, state0, cc, argAp, ap0, config) and + localCc = getLocalCc(mid, cc) + | + localStep(mid, state0, node, state, true, _, config, localCc) and + ap = ap0 + or + localStep(mid, state0, node, state, false, ap, config, localCc) and + ap0 instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + jumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStateStep(mid, state0, node, state, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + // store + exists(TypedContent tc, Ap ap0 | + fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + ap = apCons(tc, ap0) + ) + or + // read + exists(Ap ap0, Content c | + fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowConsCand(ap0, c, ap, config) + ) + or + // flow into a callable + exists(ApApprox apa | + fwdFlowIn(_, node, state, _, cc, _, ap, config) and + apa = getApprox(ap) and + if PrevStage::parameterMayFlowThrough(node, _, apa, config) + then argAp = apSome(ap) + else argAp = apNone() + ) + or + // flow out of a callable + fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) + ) + } - ApOption apNone() { result = TBooleanNone() } + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + exists(DataFlowType contentType | + fwdFlow(node1, state, cc, argAp, ap1, config) and + PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and + typecheckStore(ap1, contentType) + ) + } - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + /** + * Holds if forward flow with access path `tail` reaches a store of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(TypedContent tc | + fwdFlowStore(_, tail, tc, _, _, _, _, config) and + tc.getContent() = c and + cons = apCons(tc, tail) + ) + } + pragma[nomagic] + private predicate fwdFlowRead( + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + fwdFlow(node1, state, cc, argAp, ap, config) and + PrevStage::readStepCand(node1, c, node2, config) and + getHeadContent(ap) = c + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, + Ap ap, Configuration config + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, state, outercc, argAp, ap, config) and + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutNotFromArg( + NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + ) { + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, argAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + ccOut = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + ) + } + + pragma[nomagic] + private predicate storeStepFwd( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config + ) { + fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + ap2 = apCons(tc, ap1) and + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + } + + private predicate readStepFwd( + NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config + ) { + fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowConsCand(ap1, c, ap2, config) + } + + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and + fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), + pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + + pragma[nomagic] + private predicate returnNodeMayFlowThrough( + RetNodeEx ret, FlowState state, Ap ap, Configuration config + ) { + fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from the + * enclosing callable in order to reach a sink, and if so, `returnAp` records + * the access path of the returned value. + */ + pragma[nomagic] + predicate revFlow( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + revFlow0(node, state, toReturn, returnAp, ap, config) and + fwdFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + fwdFlow(node, state, _, _, ap, config) and + sinkNode(node, state, config) and + (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid, FlowState state0 | + localStep(node, state, mid, state0, true, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, ap, config) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, state, _, _, ap, config) and + toReturn = false and + returnAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStep(node, mid, config) and + revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStateStep(node, state, mid, state0, config) and + revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, + pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowConsCand(ap0, c, ap, config) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, state, toReturn, returnAp, ap0, config) and + readStepFwd(node, ap, _, mid, ap0, config) + ) + or + // flow into a callable + revFlowInNotToReturn(node, state, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + or + // flow out of a callable + revFlowOut(_, node, state, _, _, ap, config) and + toReturn = true and + if returnNodeMayFlowThrough(node, state, ap, config) + then returnAp = apSome(ap) + else returnAp = apNone() + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, + boolean toReturn, ApOption returnAp, Configuration config + ) { + revFlow(mid, state, toReturn, returnAp, ap0, config) and + storeStepFwd(node, ap, tc, mid, ap0, config) and + tc.getContent() = c + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, _, tail, config) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0, config) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, + Configuration config + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, state, toReturn, returnAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInNotToReturn( + ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, FlowState state, CcCall ccc | + revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and + fwdFlow(ret, state, ccc, apSome(_), ap, config) and + matchesCall(ccc, call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Ap ap2, Content c | + PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and + revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and + revFlowConsCand(ap2, c, ap1, config) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and + readStepFwd(node1, ap1, c, node2, ap2, config) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, + pragma[only_bind_into](config)) + ) + } + + predicate revFlow(NodeEx node, FlowState state, Configuration config) { + revFlow(node, state, _, _, _, config) + } + + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, ap, config) + } + + private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepFwd(_, ap, tc, _, _, config) + } + + private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepCand(_, ap, tc, _, _, config) + } + + private predicate validAp(Ap ap, Configuration config) { + revFlow(_, _, _, _, ap, config) and ap instanceof ApNil + or + exists(TypedContent head, Ap tail | + consCand(head, tail, config) and + ap = apCons(head, tail) + ) + } + + predicate consCand(TypedContent tc, Ap ap, Configuration config) { + revConsCand(tc, ap, config) and + validAp(ap, config) + } + + pragma[noinline] + private predicate parameterFlow( + ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ) { + revFlow(p, _, true, apSome(ap0), ap, config) and + c = p.getEnclosingCallable() + } + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | + parameterFlow(p, ap, ap0, c, config) and + c = ret.getEnclosingCallable() and + revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), + pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and + fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and + kind = ret.getKind() and + p.getPosition() = pos and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists( + Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + | + revFlow(arg, state, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + + predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, argAp, ap, config) + ) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | consCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and + states = count(FlowState state | revFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | + revFlow(n, state, b, retAp, ap, config) + ) + } + /* End: Stage logic. */ + } +} + +private module BooleanCallContext { + class Cc extends boolean { + Cc() { this in [true, false] } + } + + class CcCall extends Cc { + CcCall() { this = true } + } + + /** Holds if the call context may be `call`. */ + predicate matchesCall(CcCall cc, DataFlowCall call) { any() } + + class CcNoCall extends Cc { + CcNoCall() { this = false } + } + + Cc ccNone() { result = false } + + CcCall ccSomeCall() { result = true } + + class LocalCc = Unit; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } +} + +private module Level1CallContext { class Cc = CallContext; class CcCall = CallContextCall; + pragma[inline] + predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } + class CcNoCall = CallContextNoCall; Cc ccNone() { result instanceof CallContextAny } CcCall ccSomeCall() { result instanceof CallContextSomeCall } - private class LocalCc = Unit; + module NoLocalCallContext { + class LocalCc = Unit; - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSiteDispatch(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSiteDispatch(call, c) + then result = TSpecificCall(call) + else result = TSomeCall() + } + } + + module LocalCallContext { + class LocalCc = LocalCallContext; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { + result = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + node.getEnclosingCallable()) + } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() + } } bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { checkCallContextReturn(innercc, c, call) and if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() } +} - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } +private module Stage2Param implements MkStage::StageParam { + private module PrevStage = Stage1; + + class Ap extends boolean { + Ap() { this in [true, false] } + } + + class ApNil extends Ap { + ApNil() { this = false } + } + + bindingset[result, ap] + PrevStage::Ap getApprox(Ap ap) { any() } + + ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + + pragma[inline] + Content getHeadContent(Ap ap) { exists(result) and ap = true } + + class ApOption = BooleanOption; + + ApOption apNone() { result = TBooleanNone() } + + ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + + import Level1CallContext + import NoLocalCallContext bindingset[node1, state1, config] bindingset[node2, state2, config] - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -1221,9 +1906,9 @@ private module Stage2 { exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/5; - private predicate flowIntoCall = flowIntoCallNodeCand1/5; + predicate flowIntoCall = flowIntoCallNodeCand1/5; pragma[nomagic] private predicate expectsContentCand(NodeEx node, Configuration config) { @@ -1235,7 +1920,7 @@ private module Stage2 { } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and not stateBarrier(node, state, config) and @@ -1248,544 +1933,11 @@ private module Stage2 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 2 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 2 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage2 = MkStage::Stage; + pragma[nomagic] private predicate flowOutOfCallNodeCand2( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config @@ -1883,14 +2035,13 @@ private module LocalFlowBigStep { ) { additionalLocalFlowStepNodeCand1(node1, node2, config) and state1 = state2 and - Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false, - pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false, + Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false, pragma[only_bind_into](config)) or additionalLocalStateStep(node1, state1, node2, state2, config) and - Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config)) + Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config)) } /** @@ -1967,26 +2118,24 @@ private module LocalFlowBigStep { private import LocalFlowBigStep -private module Stage3 { - module PrevStage = Stage2; - - class ApApprox = PrevStage::Ap; +private module Stage3Param implements MkStage::StageParam { + private module PrevStage = Stage2; class Ap = AccessPathFront; class ApNil = AccessPathFrontNil; - private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } + PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } + Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathFrontOption; @@ -1994,44 +2143,18 @@ private module Stage3 { ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - class Cc = boolean; + import BooleanCallContext - class CcCall extends Cc { - CcCall() { this = true } - - /** Holds if this call context may be `call`. */ - predicate matchesCall(DataFlowCall call) { any() } - } - - class CcNoCall extends Cc { - CcNoCall() { this = false } - } - - Cc ccNone() { result = false } - - CcCall ccSomeCall() { result = true } - - private class LocalCc = Unit; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/5; - private predicate flowIntoCall = flowIntoCallNodeCand2/5; + predicate flowIntoCall = flowIntoCallNodeCand2/5; pragma[nomagic] private predicate clearSet(NodeEx node, ContentSet c, Configuration config) { @@ -2067,7 +2190,7 @@ private module Stage3 { private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { exists(state) and exists(config) and not clear(node, ap, config) and @@ -2080,548 +2203,15 @@ private module Stage3 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { + predicate typecheckStore(Ap ap, DataFlowType contentType) { // We need to typecheck stores here, since reverse flow through a getter // might have a different type here compared to inside the getter. compatibleTypes(ap.getType(), contentType) } - - /* Begin: Stage 3 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 3 logic. */ } +private module Stage3 = MkStage::Stage; + /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` * and remains relevant for the following pruning stage. @@ -2644,7 +2234,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and nodes = strictcount(NodeEx n, FlowState state | - Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) or flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) ) and @@ -2828,26 +2418,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } } -private module Stage4 { - module PrevStage = Stage3; - - class ApApprox = PrevStage::Ap; +private module Stage4Param implements MkStage::StageParam { + private module PrevStage = Stage3; class Ap = AccessPathApprox; class ApNil = AccessPathApproxNil; - private ApApprox getApprox(Ap ap) { result = ap.getFront() } + PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } + Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathApproxOption; @@ -2855,38 +2443,10 @@ private module Stage4 { ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - class Cc = CallContext; + import Level1CallContext + import LocalCallContext - class CcCall = CallContextCall; - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - private class LocalCc = LocalCallContext; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() - } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { - result = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - node.getEnclosingCallable()) - } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -2894,575 +2454,40 @@ private module Stage4 { } pragma[nomagic] - private predicate flowOutOfCall( + predicate flowOutOfCall( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } pragma[nomagic] - private predicate flowIntoCall( + predicate flowIntoCall( DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } // Type checking is not necessary here as it has already been done in stage 3. bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 4 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 4 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage4 = MkStage::Stage; + bindingset[conf, result] private Configuration unbindConf(Configuration conf) { exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) @@ -3495,7 +2520,7 @@ private newtype TSummaryCtx = TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and - Stage4::revFlow(p, state, _, _, _, config) + Stage4::revFlow(p, state, _, config) ) } @@ -3553,7 +2578,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { result = strictcount(NodeEx n, FlowState state | - Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config) + Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config) ) } @@ -3667,7 +2692,7 @@ private newtype TPathNode = exists(PathNodeMid mid | pathStep(mid, node, state, cc, sc, ap) and pragma[only_bind_into](config) = mid.getConfiguration() and - Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config)) + Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config)) ) } or TPathNodeSink(NodeEx node, FlowState state, Configuration config) { @@ -4207,7 +3232,7 @@ private NodeEx getAnOutNodeFlow( ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result.asNode() = kind.getAnOutNode(call) and - Stage4::revFlow(result, _, _, _, apa, config) + Stage4::revFlow(result, _, apa, config) } /** @@ -4243,7 +3268,7 @@ private predicate parameterCand( DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config ) { exists(ParamNodeEx p | - Stage4::revFlow(p, _, _, _, apa, config) and + Stage4::revFlow(p, _, apa, config) and p.isParameterOf(callable, pos) ) } diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll index a076f7a2e45..22c3bd2466e 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,12 +944,9 @@ private module Stage1 { predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) } bindingset[node, state, config] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, toReturn, pragma[only_bind_into](config)) and + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, _, pragma[only_bind_into](config)) and exists(state) and - exists(returnAp) and exists(ap) } @@ -1142,66 +1139,754 @@ private predicate flowIntoCallNodeCand1( ) } -private module Stage2 { - module PrevStage = Stage1; +private signature module StageSig { + class Ap; + predicate revFlow(NodeEx node, Configuration config); + + bindingset[node, state, config] + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config); + + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ); + + predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config); +} + +private module MkStage { class ApApprox = PrevStage::Ap; - class Ap = boolean; + signature module StageParam { + class Ap; - class ApNil extends Ap { - ApNil() { this = false } + class ApNil extends Ap; + + bindingset[result, ap] + ApApprox getApprox(Ap ap); + + ApNil getApNil(NodeEx node); + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail); + + Content getHeadContent(Ap ap); + + class ApOption; + + ApOption apNone(); + + ApOption apSome(Ap ap); + + class Cc; + + class CcCall extends Cc; + + // TODO: member predicate on CcCall + predicate matchesCall(CcCall cc, DataFlowCall call); + + class CcNoCall extends Cc; + + Cc ccNone(); + + CcCall ccSomeCall(); + + class LocalCc; + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc); + + bindingset[node1, state1, config] + bindingset[node2, state2, config] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ); + + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + ); + + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ); + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config); + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType); } - bindingset[result, ap] - private ApApprox getApprox(Ap ap) { any() } + module Stage implements StageSig { + import Param - private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) } + /* Begin: Stage logic. */ + bindingset[result, apa] + private ApApprox unbindApa(ApApprox apa) { + pragma[only_bind_out](apa) = pragma[only_bind_out](result) + } - bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, + Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, + pragma[only_bind_into](config)) and + matchesCall(ccc, call) + } - pragma[inline] - private Content getHeadContent(Ap ap) { exists(result) and ap = true } + /** + * Holds if `node` is reachable with access path `ap` from a source in the + * configuration `config`. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. + */ + pragma[nomagic] + predicate fwdFlow( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + fwdFlow0(node, state, cc, argAp, ap, config) and + PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and + filter(node, state, ap, config) + } - class ApOption = BooleanOption; + pragma[nomagic] + private predicate fwdFlow0( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + sourceNode(node, state, config) and + (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and + argAp = apNone() and + ap = getApNil(node) + or + exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | + fwdFlow(mid, state0, cc, argAp, ap0, config) and + localCc = getLocalCc(mid, cc) + | + localStep(mid, state0, node, state, true, _, config, localCc) and + ap = ap0 + or + localStep(mid, state0, node, state, false, ap, config, localCc) and + ap0 instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + jumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStateStep(mid, state0, node, state, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + // store + exists(TypedContent tc, Ap ap0 | + fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + ap = apCons(tc, ap0) + ) + or + // read + exists(Ap ap0, Content c | + fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowConsCand(ap0, c, ap, config) + ) + or + // flow into a callable + exists(ApApprox apa | + fwdFlowIn(_, node, state, _, cc, _, ap, config) and + apa = getApprox(ap) and + if PrevStage::parameterMayFlowThrough(node, _, apa, config) + then argAp = apSome(ap) + else argAp = apNone() + ) + or + // flow out of a callable + fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) + ) + } - ApOption apNone() { result = TBooleanNone() } + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + exists(DataFlowType contentType | + fwdFlow(node1, state, cc, argAp, ap1, config) and + PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and + typecheckStore(ap1, contentType) + ) + } - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + /** + * Holds if forward flow with access path `tail` reaches a store of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(TypedContent tc | + fwdFlowStore(_, tail, tc, _, _, _, _, config) and + tc.getContent() = c and + cons = apCons(tc, tail) + ) + } + pragma[nomagic] + private predicate fwdFlowRead( + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + fwdFlow(node1, state, cc, argAp, ap, config) and + PrevStage::readStepCand(node1, c, node2, config) and + getHeadContent(ap) = c + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, + Ap ap, Configuration config + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, state, outercc, argAp, ap, config) and + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutNotFromArg( + NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + ) { + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, argAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + ccOut = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + ) + } + + pragma[nomagic] + private predicate storeStepFwd( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config + ) { + fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + ap2 = apCons(tc, ap1) and + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + } + + private predicate readStepFwd( + NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config + ) { + fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowConsCand(ap1, c, ap2, config) + } + + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and + fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), + pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + + pragma[nomagic] + private predicate returnNodeMayFlowThrough( + RetNodeEx ret, FlowState state, Ap ap, Configuration config + ) { + fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from the + * enclosing callable in order to reach a sink, and if so, `returnAp` records + * the access path of the returned value. + */ + pragma[nomagic] + predicate revFlow( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + revFlow0(node, state, toReturn, returnAp, ap, config) and + fwdFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + fwdFlow(node, state, _, _, ap, config) and + sinkNode(node, state, config) and + (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid, FlowState state0 | + localStep(node, state, mid, state0, true, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, ap, config) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, state, _, _, ap, config) and + toReturn = false and + returnAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStep(node, mid, config) and + revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStateStep(node, state, mid, state0, config) and + revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, + pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowConsCand(ap0, c, ap, config) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, state, toReturn, returnAp, ap0, config) and + readStepFwd(node, ap, _, mid, ap0, config) + ) + or + // flow into a callable + revFlowInNotToReturn(node, state, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + or + // flow out of a callable + revFlowOut(_, node, state, _, _, ap, config) and + toReturn = true and + if returnNodeMayFlowThrough(node, state, ap, config) + then returnAp = apSome(ap) + else returnAp = apNone() + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, + boolean toReturn, ApOption returnAp, Configuration config + ) { + revFlow(mid, state, toReturn, returnAp, ap0, config) and + storeStepFwd(node, ap, tc, mid, ap0, config) and + tc.getContent() = c + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, _, tail, config) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0, config) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, + Configuration config + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, state, toReturn, returnAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInNotToReturn( + ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, FlowState state, CcCall ccc | + revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and + fwdFlow(ret, state, ccc, apSome(_), ap, config) and + matchesCall(ccc, call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Ap ap2, Content c | + PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and + revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and + revFlowConsCand(ap2, c, ap1, config) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and + readStepFwd(node1, ap1, c, node2, ap2, config) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, + pragma[only_bind_into](config)) + ) + } + + predicate revFlow(NodeEx node, FlowState state, Configuration config) { + revFlow(node, state, _, _, _, config) + } + + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, ap, config) + } + + private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepFwd(_, ap, tc, _, _, config) + } + + private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepCand(_, ap, tc, _, _, config) + } + + private predicate validAp(Ap ap, Configuration config) { + revFlow(_, _, _, _, ap, config) and ap instanceof ApNil + or + exists(TypedContent head, Ap tail | + consCand(head, tail, config) and + ap = apCons(head, tail) + ) + } + + predicate consCand(TypedContent tc, Ap ap, Configuration config) { + revConsCand(tc, ap, config) and + validAp(ap, config) + } + + pragma[noinline] + private predicate parameterFlow( + ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ) { + revFlow(p, _, true, apSome(ap0), ap, config) and + c = p.getEnclosingCallable() + } + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | + parameterFlow(p, ap, ap0, c, config) and + c = ret.getEnclosingCallable() and + revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), + pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and + fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and + kind = ret.getKind() and + p.getPosition() = pos and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists( + Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + | + revFlow(arg, state, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + + predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, argAp, ap, config) + ) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | consCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and + states = count(FlowState state | revFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | + revFlow(n, state, b, retAp, ap, config) + ) + } + /* End: Stage logic. */ + } +} + +private module BooleanCallContext { + class Cc extends boolean { + Cc() { this in [true, false] } + } + + class CcCall extends Cc { + CcCall() { this = true } + } + + /** Holds if the call context may be `call`. */ + predicate matchesCall(CcCall cc, DataFlowCall call) { any() } + + class CcNoCall extends Cc { + CcNoCall() { this = false } + } + + Cc ccNone() { result = false } + + CcCall ccSomeCall() { result = true } + + class LocalCc = Unit; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } +} + +private module Level1CallContext { class Cc = CallContext; class CcCall = CallContextCall; + pragma[inline] + predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } + class CcNoCall = CallContextNoCall; Cc ccNone() { result instanceof CallContextAny } CcCall ccSomeCall() { result instanceof CallContextSomeCall } - private class LocalCc = Unit; + module NoLocalCallContext { + class LocalCc = Unit; - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSiteDispatch(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSiteDispatch(call, c) + then result = TSpecificCall(call) + else result = TSomeCall() + } + } + + module LocalCallContext { + class LocalCc = LocalCallContext; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { + result = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + node.getEnclosingCallable()) + } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() + } } bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { checkCallContextReturn(innercc, c, call) and if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() } +} - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } +private module Stage2Param implements MkStage::StageParam { + private module PrevStage = Stage1; + + class Ap extends boolean { + Ap() { this in [true, false] } + } + + class ApNil extends Ap { + ApNil() { this = false } + } + + bindingset[result, ap] + PrevStage::Ap getApprox(Ap ap) { any() } + + ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + + pragma[inline] + Content getHeadContent(Ap ap) { exists(result) and ap = true } + + class ApOption = BooleanOption; + + ApOption apNone() { result = TBooleanNone() } + + ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + + import Level1CallContext + import NoLocalCallContext bindingset[node1, state1, config] bindingset[node2, state2, config] - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -1221,9 +1906,9 @@ private module Stage2 { exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/5; - private predicate flowIntoCall = flowIntoCallNodeCand1/5; + predicate flowIntoCall = flowIntoCallNodeCand1/5; pragma[nomagic] private predicate expectsContentCand(NodeEx node, Configuration config) { @@ -1235,7 +1920,7 @@ private module Stage2 { } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and not stateBarrier(node, state, config) and @@ -1248,544 +1933,11 @@ private module Stage2 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 2 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 2 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage2 = MkStage::Stage; + pragma[nomagic] private predicate flowOutOfCallNodeCand2( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config @@ -1883,14 +2035,13 @@ private module LocalFlowBigStep { ) { additionalLocalFlowStepNodeCand1(node1, node2, config) and state1 = state2 and - Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false, - pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false, + Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false, pragma[only_bind_into](config)) or additionalLocalStateStep(node1, state1, node2, state2, config) and - Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config)) + Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config)) } /** @@ -1967,26 +2118,24 @@ private module LocalFlowBigStep { private import LocalFlowBigStep -private module Stage3 { - module PrevStage = Stage2; - - class ApApprox = PrevStage::Ap; +private module Stage3Param implements MkStage::StageParam { + private module PrevStage = Stage2; class Ap = AccessPathFront; class ApNil = AccessPathFrontNil; - private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } + PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } + Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathFrontOption; @@ -1994,44 +2143,18 @@ private module Stage3 { ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - class Cc = boolean; + import BooleanCallContext - class CcCall extends Cc { - CcCall() { this = true } - - /** Holds if this call context may be `call`. */ - predicate matchesCall(DataFlowCall call) { any() } - } - - class CcNoCall extends Cc { - CcNoCall() { this = false } - } - - Cc ccNone() { result = false } - - CcCall ccSomeCall() { result = true } - - private class LocalCc = Unit; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/5; - private predicate flowIntoCall = flowIntoCallNodeCand2/5; + predicate flowIntoCall = flowIntoCallNodeCand2/5; pragma[nomagic] private predicate clearSet(NodeEx node, ContentSet c, Configuration config) { @@ -2067,7 +2190,7 @@ private module Stage3 { private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { exists(state) and exists(config) and not clear(node, ap, config) and @@ -2080,548 +2203,15 @@ private module Stage3 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { + predicate typecheckStore(Ap ap, DataFlowType contentType) { // We need to typecheck stores here, since reverse flow through a getter // might have a different type here compared to inside the getter. compatibleTypes(ap.getType(), contentType) } - - /* Begin: Stage 3 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 3 logic. */ } +private module Stage3 = MkStage::Stage; + /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` * and remains relevant for the following pruning stage. @@ -2644,7 +2234,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and nodes = strictcount(NodeEx n, FlowState state | - Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) or flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) ) and @@ -2828,26 +2418,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } } -private module Stage4 { - module PrevStage = Stage3; - - class ApApprox = PrevStage::Ap; +private module Stage4Param implements MkStage::StageParam { + private module PrevStage = Stage3; class Ap = AccessPathApprox; class ApNil = AccessPathApproxNil; - private ApApprox getApprox(Ap ap) { result = ap.getFront() } + PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } + Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathApproxOption; @@ -2855,38 +2443,10 @@ private module Stage4 { ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - class Cc = CallContext; + import Level1CallContext + import LocalCallContext - class CcCall = CallContextCall; - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - private class LocalCc = LocalCallContext; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() - } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { - result = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - node.getEnclosingCallable()) - } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -2894,575 +2454,40 @@ private module Stage4 { } pragma[nomagic] - private predicate flowOutOfCall( + predicate flowOutOfCall( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } pragma[nomagic] - private predicate flowIntoCall( + predicate flowIntoCall( DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } // Type checking is not necessary here as it has already been done in stage 3. bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 4 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 4 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage4 = MkStage::Stage; + bindingset[conf, result] private Configuration unbindConf(Configuration conf) { exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) @@ -3495,7 +2520,7 @@ private newtype TSummaryCtx = TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and - Stage4::revFlow(p, state, _, _, _, config) + Stage4::revFlow(p, state, _, config) ) } @@ -3553,7 +2578,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { result = strictcount(NodeEx n, FlowState state | - Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config) + Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config) ) } @@ -3667,7 +2692,7 @@ private newtype TPathNode = exists(PathNodeMid mid | pathStep(mid, node, state, cc, sc, ap) and pragma[only_bind_into](config) = mid.getConfiguration() and - Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config)) + Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config)) ) } or TPathNodeSink(NodeEx node, FlowState state, Configuration config) { @@ -4207,7 +3232,7 @@ private NodeEx getAnOutNodeFlow( ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result.asNode() = kind.getAnOutNode(call) and - Stage4::revFlow(result, _, _, _, apa, config) + Stage4::revFlow(result, _, apa, config) } /** @@ -4243,7 +3268,7 @@ private predicate parameterCand( DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config ) { exists(ParamNodeEx p | - Stage4::revFlow(p, _, _, _, apa, config) and + Stage4::revFlow(p, _, apa, config) and p.isParameterOf(callable, pos) ) } diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll index a076f7a2e45..22c3bd2466e 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,12 +944,9 @@ private module Stage1 { predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) } bindingset[node, state, config] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, toReturn, pragma[only_bind_into](config)) and + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, _, pragma[only_bind_into](config)) and exists(state) and - exists(returnAp) and exists(ap) } @@ -1142,66 +1139,754 @@ private predicate flowIntoCallNodeCand1( ) } -private module Stage2 { - module PrevStage = Stage1; +private signature module StageSig { + class Ap; + predicate revFlow(NodeEx node, Configuration config); + + bindingset[node, state, config] + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config); + + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ); + + predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config); +} + +private module MkStage { class ApApprox = PrevStage::Ap; - class Ap = boolean; + signature module StageParam { + class Ap; - class ApNil extends Ap { - ApNil() { this = false } + class ApNil extends Ap; + + bindingset[result, ap] + ApApprox getApprox(Ap ap); + + ApNil getApNil(NodeEx node); + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail); + + Content getHeadContent(Ap ap); + + class ApOption; + + ApOption apNone(); + + ApOption apSome(Ap ap); + + class Cc; + + class CcCall extends Cc; + + // TODO: member predicate on CcCall + predicate matchesCall(CcCall cc, DataFlowCall call); + + class CcNoCall extends Cc; + + Cc ccNone(); + + CcCall ccSomeCall(); + + class LocalCc; + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc); + + bindingset[node1, state1, config] + bindingset[node2, state2, config] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ); + + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + ); + + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ); + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config); + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType); } - bindingset[result, ap] - private ApApprox getApprox(Ap ap) { any() } + module Stage implements StageSig { + import Param - private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) } + /* Begin: Stage logic. */ + bindingset[result, apa] + private ApApprox unbindApa(ApApprox apa) { + pragma[only_bind_out](apa) = pragma[only_bind_out](result) + } - bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, + Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, + pragma[only_bind_into](config)) and + matchesCall(ccc, call) + } - pragma[inline] - private Content getHeadContent(Ap ap) { exists(result) and ap = true } + /** + * Holds if `node` is reachable with access path `ap` from a source in the + * configuration `config`. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. + */ + pragma[nomagic] + predicate fwdFlow( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + fwdFlow0(node, state, cc, argAp, ap, config) and + PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and + filter(node, state, ap, config) + } - class ApOption = BooleanOption; + pragma[nomagic] + private predicate fwdFlow0( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + sourceNode(node, state, config) and + (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and + argAp = apNone() and + ap = getApNil(node) + or + exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | + fwdFlow(mid, state0, cc, argAp, ap0, config) and + localCc = getLocalCc(mid, cc) + | + localStep(mid, state0, node, state, true, _, config, localCc) and + ap = ap0 + or + localStep(mid, state0, node, state, false, ap, config, localCc) and + ap0 instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + jumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStateStep(mid, state0, node, state, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + // store + exists(TypedContent tc, Ap ap0 | + fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + ap = apCons(tc, ap0) + ) + or + // read + exists(Ap ap0, Content c | + fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowConsCand(ap0, c, ap, config) + ) + or + // flow into a callable + exists(ApApprox apa | + fwdFlowIn(_, node, state, _, cc, _, ap, config) and + apa = getApprox(ap) and + if PrevStage::parameterMayFlowThrough(node, _, apa, config) + then argAp = apSome(ap) + else argAp = apNone() + ) + or + // flow out of a callable + fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) + ) + } - ApOption apNone() { result = TBooleanNone() } + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + exists(DataFlowType contentType | + fwdFlow(node1, state, cc, argAp, ap1, config) and + PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and + typecheckStore(ap1, contentType) + ) + } - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + /** + * Holds if forward flow with access path `tail` reaches a store of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(TypedContent tc | + fwdFlowStore(_, tail, tc, _, _, _, _, config) and + tc.getContent() = c and + cons = apCons(tc, tail) + ) + } + pragma[nomagic] + private predicate fwdFlowRead( + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + fwdFlow(node1, state, cc, argAp, ap, config) and + PrevStage::readStepCand(node1, c, node2, config) and + getHeadContent(ap) = c + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, + Ap ap, Configuration config + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, state, outercc, argAp, ap, config) and + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutNotFromArg( + NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + ) { + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, argAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + ccOut = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + ) + } + + pragma[nomagic] + private predicate storeStepFwd( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config + ) { + fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + ap2 = apCons(tc, ap1) and + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + } + + private predicate readStepFwd( + NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config + ) { + fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowConsCand(ap1, c, ap2, config) + } + + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and + fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), + pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + + pragma[nomagic] + private predicate returnNodeMayFlowThrough( + RetNodeEx ret, FlowState state, Ap ap, Configuration config + ) { + fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from the + * enclosing callable in order to reach a sink, and if so, `returnAp` records + * the access path of the returned value. + */ + pragma[nomagic] + predicate revFlow( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + revFlow0(node, state, toReturn, returnAp, ap, config) and + fwdFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + fwdFlow(node, state, _, _, ap, config) and + sinkNode(node, state, config) and + (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid, FlowState state0 | + localStep(node, state, mid, state0, true, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, ap, config) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, state, _, _, ap, config) and + toReturn = false and + returnAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStep(node, mid, config) and + revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStateStep(node, state, mid, state0, config) and + revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, + pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowConsCand(ap0, c, ap, config) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, state, toReturn, returnAp, ap0, config) and + readStepFwd(node, ap, _, mid, ap0, config) + ) + or + // flow into a callable + revFlowInNotToReturn(node, state, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + or + // flow out of a callable + revFlowOut(_, node, state, _, _, ap, config) and + toReturn = true and + if returnNodeMayFlowThrough(node, state, ap, config) + then returnAp = apSome(ap) + else returnAp = apNone() + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, + boolean toReturn, ApOption returnAp, Configuration config + ) { + revFlow(mid, state, toReturn, returnAp, ap0, config) and + storeStepFwd(node, ap, tc, mid, ap0, config) and + tc.getContent() = c + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, _, tail, config) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0, config) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, + Configuration config + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, state, toReturn, returnAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInNotToReturn( + ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, FlowState state, CcCall ccc | + revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and + fwdFlow(ret, state, ccc, apSome(_), ap, config) and + matchesCall(ccc, call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Ap ap2, Content c | + PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and + revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and + revFlowConsCand(ap2, c, ap1, config) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and + readStepFwd(node1, ap1, c, node2, ap2, config) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, + pragma[only_bind_into](config)) + ) + } + + predicate revFlow(NodeEx node, FlowState state, Configuration config) { + revFlow(node, state, _, _, _, config) + } + + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, ap, config) + } + + private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepFwd(_, ap, tc, _, _, config) + } + + private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepCand(_, ap, tc, _, _, config) + } + + private predicate validAp(Ap ap, Configuration config) { + revFlow(_, _, _, _, ap, config) and ap instanceof ApNil + or + exists(TypedContent head, Ap tail | + consCand(head, tail, config) and + ap = apCons(head, tail) + ) + } + + predicate consCand(TypedContent tc, Ap ap, Configuration config) { + revConsCand(tc, ap, config) and + validAp(ap, config) + } + + pragma[noinline] + private predicate parameterFlow( + ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ) { + revFlow(p, _, true, apSome(ap0), ap, config) and + c = p.getEnclosingCallable() + } + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | + parameterFlow(p, ap, ap0, c, config) and + c = ret.getEnclosingCallable() and + revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), + pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and + fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and + kind = ret.getKind() and + p.getPosition() = pos and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists( + Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + | + revFlow(arg, state, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + + predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, argAp, ap, config) + ) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | consCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and + states = count(FlowState state | revFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | + revFlow(n, state, b, retAp, ap, config) + ) + } + /* End: Stage logic. */ + } +} + +private module BooleanCallContext { + class Cc extends boolean { + Cc() { this in [true, false] } + } + + class CcCall extends Cc { + CcCall() { this = true } + } + + /** Holds if the call context may be `call`. */ + predicate matchesCall(CcCall cc, DataFlowCall call) { any() } + + class CcNoCall extends Cc { + CcNoCall() { this = false } + } + + Cc ccNone() { result = false } + + CcCall ccSomeCall() { result = true } + + class LocalCc = Unit; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } +} + +private module Level1CallContext { class Cc = CallContext; class CcCall = CallContextCall; + pragma[inline] + predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } + class CcNoCall = CallContextNoCall; Cc ccNone() { result instanceof CallContextAny } CcCall ccSomeCall() { result instanceof CallContextSomeCall } - private class LocalCc = Unit; + module NoLocalCallContext { + class LocalCc = Unit; - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSiteDispatch(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSiteDispatch(call, c) + then result = TSpecificCall(call) + else result = TSomeCall() + } + } + + module LocalCallContext { + class LocalCc = LocalCallContext; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { + result = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + node.getEnclosingCallable()) + } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() + } } bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { checkCallContextReturn(innercc, c, call) and if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() } +} - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } +private module Stage2Param implements MkStage::StageParam { + private module PrevStage = Stage1; + + class Ap extends boolean { + Ap() { this in [true, false] } + } + + class ApNil extends Ap { + ApNil() { this = false } + } + + bindingset[result, ap] + PrevStage::Ap getApprox(Ap ap) { any() } + + ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + + pragma[inline] + Content getHeadContent(Ap ap) { exists(result) and ap = true } + + class ApOption = BooleanOption; + + ApOption apNone() { result = TBooleanNone() } + + ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + + import Level1CallContext + import NoLocalCallContext bindingset[node1, state1, config] bindingset[node2, state2, config] - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -1221,9 +1906,9 @@ private module Stage2 { exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/5; - private predicate flowIntoCall = flowIntoCallNodeCand1/5; + predicate flowIntoCall = flowIntoCallNodeCand1/5; pragma[nomagic] private predicate expectsContentCand(NodeEx node, Configuration config) { @@ -1235,7 +1920,7 @@ private module Stage2 { } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and not stateBarrier(node, state, config) and @@ -1248,544 +1933,11 @@ private module Stage2 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 2 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 2 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage2 = MkStage::Stage; + pragma[nomagic] private predicate flowOutOfCallNodeCand2( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config @@ -1883,14 +2035,13 @@ private module LocalFlowBigStep { ) { additionalLocalFlowStepNodeCand1(node1, node2, config) and state1 = state2 and - Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false, - pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false, + Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false, pragma[only_bind_into](config)) or additionalLocalStateStep(node1, state1, node2, state2, config) and - Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config)) + Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config)) } /** @@ -1967,26 +2118,24 @@ private module LocalFlowBigStep { private import LocalFlowBigStep -private module Stage3 { - module PrevStage = Stage2; - - class ApApprox = PrevStage::Ap; +private module Stage3Param implements MkStage::StageParam { + private module PrevStage = Stage2; class Ap = AccessPathFront; class ApNil = AccessPathFrontNil; - private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } + PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } + Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathFrontOption; @@ -1994,44 +2143,18 @@ private module Stage3 { ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - class Cc = boolean; + import BooleanCallContext - class CcCall extends Cc { - CcCall() { this = true } - - /** Holds if this call context may be `call`. */ - predicate matchesCall(DataFlowCall call) { any() } - } - - class CcNoCall extends Cc { - CcNoCall() { this = false } - } - - Cc ccNone() { result = false } - - CcCall ccSomeCall() { result = true } - - private class LocalCc = Unit; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/5; - private predicate flowIntoCall = flowIntoCallNodeCand2/5; + predicate flowIntoCall = flowIntoCallNodeCand2/5; pragma[nomagic] private predicate clearSet(NodeEx node, ContentSet c, Configuration config) { @@ -2067,7 +2190,7 @@ private module Stage3 { private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { exists(state) and exists(config) and not clear(node, ap, config) and @@ -2080,548 +2203,15 @@ private module Stage3 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { + predicate typecheckStore(Ap ap, DataFlowType contentType) { // We need to typecheck stores here, since reverse flow through a getter // might have a different type here compared to inside the getter. compatibleTypes(ap.getType(), contentType) } - - /* Begin: Stage 3 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 3 logic. */ } +private module Stage3 = MkStage::Stage; + /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` * and remains relevant for the following pruning stage. @@ -2644,7 +2234,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and nodes = strictcount(NodeEx n, FlowState state | - Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) or flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) ) and @@ -2828,26 +2418,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } } -private module Stage4 { - module PrevStage = Stage3; - - class ApApprox = PrevStage::Ap; +private module Stage4Param implements MkStage::StageParam { + private module PrevStage = Stage3; class Ap = AccessPathApprox; class ApNil = AccessPathApproxNil; - private ApApprox getApprox(Ap ap) { result = ap.getFront() } + PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } + Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathApproxOption; @@ -2855,38 +2443,10 @@ private module Stage4 { ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - class Cc = CallContext; + import Level1CallContext + import LocalCallContext - class CcCall = CallContextCall; - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - private class LocalCc = LocalCallContext; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() - } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { - result = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - node.getEnclosingCallable()) - } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -2894,575 +2454,40 @@ private module Stage4 { } pragma[nomagic] - private predicate flowOutOfCall( + predicate flowOutOfCall( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } pragma[nomagic] - private predicate flowIntoCall( + predicate flowIntoCall( DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } // Type checking is not necessary here as it has already been done in stage 3. bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 4 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 4 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage4 = MkStage::Stage; + bindingset[conf, result] private Configuration unbindConf(Configuration conf) { exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) @@ -3495,7 +2520,7 @@ private newtype TSummaryCtx = TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and - Stage4::revFlow(p, state, _, _, _, config) + Stage4::revFlow(p, state, _, config) ) } @@ -3553,7 +2578,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { result = strictcount(NodeEx n, FlowState state | - Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config) + Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config) ) } @@ -3667,7 +2692,7 @@ private newtype TPathNode = exists(PathNodeMid mid | pathStep(mid, node, state, cc, sc, ap) and pragma[only_bind_into](config) = mid.getConfiguration() and - Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config)) + Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config)) ) } or TPathNodeSink(NodeEx node, FlowState state, Configuration config) { @@ -4207,7 +3232,7 @@ private NodeEx getAnOutNodeFlow( ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result.asNode() = kind.getAnOutNode(call) and - Stage4::revFlow(result, _, _, _, apa, config) + Stage4::revFlow(result, _, apa, config) } /** @@ -4243,7 +3268,7 @@ private predicate parameterCand( DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config ) { exists(ParamNodeEx p | - Stage4::revFlow(p, _, _, _, apa, config) and + Stage4::revFlow(p, _, apa, config) and p.isParameterOf(callable, pos) ) } diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll index a076f7a2e45..22c3bd2466e 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,12 +944,9 @@ private module Stage1 { predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) } bindingset[node, state, config] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, toReturn, pragma[only_bind_into](config)) and + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, _, pragma[only_bind_into](config)) and exists(state) and - exists(returnAp) and exists(ap) } @@ -1142,66 +1139,754 @@ private predicate flowIntoCallNodeCand1( ) } -private module Stage2 { - module PrevStage = Stage1; +private signature module StageSig { + class Ap; + predicate revFlow(NodeEx node, Configuration config); + + bindingset[node, state, config] + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config); + + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ); + + predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config); +} + +private module MkStage { class ApApprox = PrevStage::Ap; - class Ap = boolean; + signature module StageParam { + class Ap; - class ApNil extends Ap { - ApNil() { this = false } + class ApNil extends Ap; + + bindingset[result, ap] + ApApprox getApprox(Ap ap); + + ApNil getApNil(NodeEx node); + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail); + + Content getHeadContent(Ap ap); + + class ApOption; + + ApOption apNone(); + + ApOption apSome(Ap ap); + + class Cc; + + class CcCall extends Cc; + + // TODO: member predicate on CcCall + predicate matchesCall(CcCall cc, DataFlowCall call); + + class CcNoCall extends Cc; + + Cc ccNone(); + + CcCall ccSomeCall(); + + class LocalCc; + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc); + + bindingset[node1, state1, config] + bindingset[node2, state2, config] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ); + + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + ); + + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ); + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config); + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType); } - bindingset[result, ap] - private ApApprox getApprox(Ap ap) { any() } + module Stage implements StageSig { + import Param - private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) } + /* Begin: Stage logic. */ + bindingset[result, apa] + private ApApprox unbindApa(ApApprox apa) { + pragma[only_bind_out](apa) = pragma[only_bind_out](result) + } - bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, + Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, + pragma[only_bind_into](config)) and + matchesCall(ccc, call) + } - pragma[inline] - private Content getHeadContent(Ap ap) { exists(result) and ap = true } + /** + * Holds if `node` is reachable with access path `ap` from a source in the + * configuration `config`. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. + */ + pragma[nomagic] + predicate fwdFlow( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + fwdFlow0(node, state, cc, argAp, ap, config) and + PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and + filter(node, state, ap, config) + } - class ApOption = BooleanOption; + pragma[nomagic] + private predicate fwdFlow0( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + sourceNode(node, state, config) and + (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and + argAp = apNone() and + ap = getApNil(node) + or + exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | + fwdFlow(mid, state0, cc, argAp, ap0, config) and + localCc = getLocalCc(mid, cc) + | + localStep(mid, state0, node, state, true, _, config, localCc) and + ap = ap0 + or + localStep(mid, state0, node, state, false, ap, config, localCc) and + ap0 instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + jumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStateStep(mid, state0, node, state, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + // store + exists(TypedContent tc, Ap ap0 | + fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + ap = apCons(tc, ap0) + ) + or + // read + exists(Ap ap0, Content c | + fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowConsCand(ap0, c, ap, config) + ) + or + // flow into a callable + exists(ApApprox apa | + fwdFlowIn(_, node, state, _, cc, _, ap, config) and + apa = getApprox(ap) and + if PrevStage::parameterMayFlowThrough(node, _, apa, config) + then argAp = apSome(ap) + else argAp = apNone() + ) + or + // flow out of a callable + fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) + ) + } - ApOption apNone() { result = TBooleanNone() } + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + exists(DataFlowType contentType | + fwdFlow(node1, state, cc, argAp, ap1, config) and + PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and + typecheckStore(ap1, contentType) + ) + } - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + /** + * Holds if forward flow with access path `tail` reaches a store of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(TypedContent tc | + fwdFlowStore(_, tail, tc, _, _, _, _, config) and + tc.getContent() = c and + cons = apCons(tc, tail) + ) + } + pragma[nomagic] + private predicate fwdFlowRead( + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + fwdFlow(node1, state, cc, argAp, ap, config) and + PrevStage::readStepCand(node1, c, node2, config) and + getHeadContent(ap) = c + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, + Ap ap, Configuration config + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, state, outercc, argAp, ap, config) and + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutNotFromArg( + NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + ) { + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, argAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + ccOut = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + ) + } + + pragma[nomagic] + private predicate storeStepFwd( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config + ) { + fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + ap2 = apCons(tc, ap1) and + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + } + + private predicate readStepFwd( + NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config + ) { + fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowConsCand(ap1, c, ap2, config) + } + + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and + fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), + pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + + pragma[nomagic] + private predicate returnNodeMayFlowThrough( + RetNodeEx ret, FlowState state, Ap ap, Configuration config + ) { + fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from the + * enclosing callable in order to reach a sink, and if so, `returnAp` records + * the access path of the returned value. + */ + pragma[nomagic] + predicate revFlow( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + revFlow0(node, state, toReturn, returnAp, ap, config) and + fwdFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + fwdFlow(node, state, _, _, ap, config) and + sinkNode(node, state, config) and + (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid, FlowState state0 | + localStep(node, state, mid, state0, true, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, ap, config) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, state, _, _, ap, config) and + toReturn = false and + returnAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStep(node, mid, config) and + revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStateStep(node, state, mid, state0, config) and + revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, + pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowConsCand(ap0, c, ap, config) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, state, toReturn, returnAp, ap0, config) and + readStepFwd(node, ap, _, mid, ap0, config) + ) + or + // flow into a callable + revFlowInNotToReturn(node, state, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + or + // flow out of a callable + revFlowOut(_, node, state, _, _, ap, config) and + toReturn = true and + if returnNodeMayFlowThrough(node, state, ap, config) + then returnAp = apSome(ap) + else returnAp = apNone() + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, + boolean toReturn, ApOption returnAp, Configuration config + ) { + revFlow(mid, state, toReturn, returnAp, ap0, config) and + storeStepFwd(node, ap, tc, mid, ap0, config) and + tc.getContent() = c + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, _, tail, config) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0, config) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, + Configuration config + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, state, toReturn, returnAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInNotToReturn( + ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, FlowState state, CcCall ccc | + revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and + fwdFlow(ret, state, ccc, apSome(_), ap, config) and + matchesCall(ccc, call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Ap ap2, Content c | + PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and + revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and + revFlowConsCand(ap2, c, ap1, config) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and + readStepFwd(node1, ap1, c, node2, ap2, config) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, + pragma[only_bind_into](config)) + ) + } + + predicate revFlow(NodeEx node, FlowState state, Configuration config) { + revFlow(node, state, _, _, _, config) + } + + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, ap, config) + } + + private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepFwd(_, ap, tc, _, _, config) + } + + private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepCand(_, ap, tc, _, _, config) + } + + private predicate validAp(Ap ap, Configuration config) { + revFlow(_, _, _, _, ap, config) and ap instanceof ApNil + or + exists(TypedContent head, Ap tail | + consCand(head, tail, config) and + ap = apCons(head, tail) + ) + } + + predicate consCand(TypedContent tc, Ap ap, Configuration config) { + revConsCand(tc, ap, config) and + validAp(ap, config) + } + + pragma[noinline] + private predicate parameterFlow( + ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ) { + revFlow(p, _, true, apSome(ap0), ap, config) and + c = p.getEnclosingCallable() + } + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | + parameterFlow(p, ap, ap0, c, config) and + c = ret.getEnclosingCallable() and + revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), + pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and + fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and + kind = ret.getKind() and + p.getPosition() = pos and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists( + Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + | + revFlow(arg, state, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + + predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, argAp, ap, config) + ) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | consCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and + states = count(FlowState state | revFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | + revFlow(n, state, b, retAp, ap, config) + ) + } + /* End: Stage logic. */ + } +} + +private module BooleanCallContext { + class Cc extends boolean { + Cc() { this in [true, false] } + } + + class CcCall extends Cc { + CcCall() { this = true } + } + + /** Holds if the call context may be `call`. */ + predicate matchesCall(CcCall cc, DataFlowCall call) { any() } + + class CcNoCall extends Cc { + CcNoCall() { this = false } + } + + Cc ccNone() { result = false } + + CcCall ccSomeCall() { result = true } + + class LocalCc = Unit; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } +} + +private module Level1CallContext { class Cc = CallContext; class CcCall = CallContextCall; + pragma[inline] + predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } + class CcNoCall = CallContextNoCall; Cc ccNone() { result instanceof CallContextAny } CcCall ccSomeCall() { result instanceof CallContextSomeCall } - private class LocalCc = Unit; + module NoLocalCallContext { + class LocalCc = Unit; - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSiteDispatch(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSiteDispatch(call, c) + then result = TSpecificCall(call) + else result = TSomeCall() + } + } + + module LocalCallContext { + class LocalCc = LocalCallContext; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { + result = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + node.getEnclosingCallable()) + } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() + } } bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { checkCallContextReturn(innercc, c, call) and if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() } +} - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } +private module Stage2Param implements MkStage::StageParam { + private module PrevStage = Stage1; + + class Ap extends boolean { + Ap() { this in [true, false] } + } + + class ApNil extends Ap { + ApNil() { this = false } + } + + bindingset[result, ap] + PrevStage::Ap getApprox(Ap ap) { any() } + + ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + + pragma[inline] + Content getHeadContent(Ap ap) { exists(result) and ap = true } + + class ApOption = BooleanOption; + + ApOption apNone() { result = TBooleanNone() } + + ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + + import Level1CallContext + import NoLocalCallContext bindingset[node1, state1, config] bindingset[node2, state2, config] - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -1221,9 +1906,9 @@ private module Stage2 { exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/5; - private predicate flowIntoCall = flowIntoCallNodeCand1/5; + predicate flowIntoCall = flowIntoCallNodeCand1/5; pragma[nomagic] private predicate expectsContentCand(NodeEx node, Configuration config) { @@ -1235,7 +1920,7 @@ private module Stage2 { } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and not stateBarrier(node, state, config) and @@ -1248,544 +1933,11 @@ private module Stage2 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 2 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 2 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage2 = MkStage::Stage; + pragma[nomagic] private predicate flowOutOfCallNodeCand2( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config @@ -1883,14 +2035,13 @@ private module LocalFlowBigStep { ) { additionalLocalFlowStepNodeCand1(node1, node2, config) and state1 = state2 and - Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false, - pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false, + Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false, pragma[only_bind_into](config)) or additionalLocalStateStep(node1, state1, node2, state2, config) and - Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config)) + Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config)) } /** @@ -1967,26 +2118,24 @@ private module LocalFlowBigStep { private import LocalFlowBigStep -private module Stage3 { - module PrevStage = Stage2; - - class ApApprox = PrevStage::Ap; +private module Stage3Param implements MkStage::StageParam { + private module PrevStage = Stage2; class Ap = AccessPathFront; class ApNil = AccessPathFrontNil; - private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } + PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } + Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathFrontOption; @@ -1994,44 +2143,18 @@ private module Stage3 { ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - class Cc = boolean; + import BooleanCallContext - class CcCall extends Cc { - CcCall() { this = true } - - /** Holds if this call context may be `call`. */ - predicate matchesCall(DataFlowCall call) { any() } - } - - class CcNoCall extends Cc { - CcNoCall() { this = false } - } - - Cc ccNone() { result = false } - - CcCall ccSomeCall() { result = true } - - private class LocalCc = Unit; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/5; - private predicate flowIntoCall = flowIntoCallNodeCand2/5; + predicate flowIntoCall = flowIntoCallNodeCand2/5; pragma[nomagic] private predicate clearSet(NodeEx node, ContentSet c, Configuration config) { @@ -2067,7 +2190,7 @@ private module Stage3 { private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { exists(state) and exists(config) and not clear(node, ap, config) and @@ -2080,548 +2203,15 @@ private module Stage3 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { + predicate typecheckStore(Ap ap, DataFlowType contentType) { // We need to typecheck stores here, since reverse flow through a getter // might have a different type here compared to inside the getter. compatibleTypes(ap.getType(), contentType) } - - /* Begin: Stage 3 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 3 logic. */ } +private module Stage3 = MkStage::Stage; + /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` * and remains relevant for the following pruning stage. @@ -2644,7 +2234,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and nodes = strictcount(NodeEx n, FlowState state | - Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) or flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) ) and @@ -2828,26 +2418,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } } -private module Stage4 { - module PrevStage = Stage3; - - class ApApprox = PrevStage::Ap; +private module Stage4Param implements MkStage::StageParam { + private module PrevStage = Stage3; class Ap = AccessPathApprox; class ApNil = AccessPathApproxNil; - private ApApprox getApprox(Ap ap) { result = ap.getFront() } + PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } + Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathApproxOption; @@ -2855,38 +2443,10 @@ private module Stage4 { ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - class Cc = CallContext; + import Level1CallContext + import LocalCallContext - class CcCall = CallContextCall; - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - private class LocalCc = LocalCallContext; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() - } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { - result = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - node.getEnclosingCallable()) - } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -2894,575 +2454,40 @@ private module Stage4 { } pragma[nomagic] - private predicate flowOutOfCall( + predicate flowOutOfCall( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } pragma[nomagic] - private predicate flowIntoCall( + predicate flowIntoCall( DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } // Type checking is not necessary here as it has already been done in stage 3. bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 4 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 4 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage4 = MkStage::Stage; + bindingset[conf, result] private Configuration unbindConf(Configuration conf) { exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) @@ -3495,7 +2520,7 @@ private newtype TSummaryCtx = TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and - Stage4::revFlow(p, state, _, _, _, config) + Stage4::revFlow(p, state, _, config) ) } @@ -3553,7 +2578,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { result = strictcount(NodeEx n, FlowState state | - Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config) + Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config) ) } @@ -3667,7 +2692,7 @@ private newtype TPathNode = exists(PathNodeMid mid | pathStep(mid, node, state, cc, sc, ap) and pragma[only_bind_into](config) = mid.getConfiguration() and - Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config)) + Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config)) ) } or TPathNodeSink(NodeEx node, FlowState state, Configuration config) { @@ -4207,7 +3232,7 @@ private NodeEx getAnOutNodeFlow( ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result.asNode() = kind.getAnOutNode(call) and - Stage4::revFlow(result, _, _, _, apa, config) + Stage4::revFlow(result, _, apa, config) } /** @@ -4243,7 +3268,7 @@ private predicate parameterCand( DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config ) { exists(ParamNodeEx p | - Stage4::revFlow(p, _, _, _, apa, config) and + Stage4::revFlow(p, _, apa, config) and p.isParameterOf(callable, pos) ) } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll index a076f7a2e45..22c3bd2466e 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,12 +944,9 @@ private module Stage1 { predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) } bindingset[node, state, config] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, toReturn, pragma[only_bind_into](config)) and + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, _, pragma[only_bind_into](config)) and exists(state) and - exists(returnAp) and exists(ap) } @@ -1142,66 +1139,754 @@ private predicate flowIntoCallNodeCand1( ) } -private module Stage2 { - module PrevStage = Stage1; +private signature module StageSig { + class Ap; + predicate revFlow(NodeEx node, Configuration config); + + bindingset[node, state, config] + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config); + + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ); + + predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config); +} + +private module MkStage { class ApApprox = PrevStage::Ap; - class Ap = boolean; + signature module StageParam { + class Ap; - class ApNil extends Ap { - ApNil() { this = false } + class ApNil extends Ap; + + bindingset[result, ap] + ApApprox getApprox(Ap ap); + + ApNil getApNil(NodeEx node); + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail); + + Content getHeadContent(Ap ap); + + class ApOption; + + ApOption apNone(); + + ApOption apSome(Ap ap); + + class Cc; + + class CcCall extends Cc; + + // TODO: member predicate on CcCall + predicate matchesCall(CcCall cc, DataFlowCall call); + + class CcNoCall extends Cc; + + Cc ccNone(); + + CcCall ccSomeCall(); + + class LocalCc; + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc); + + bindingset[node1, state1, config] + bindingset[node2, state2, config] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ); + + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + ); + + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ); + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config); + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType); } - bindingset[result, ap] - private ApApprox getApprox(Ap ap) { any() } + module Stage implements StageSig { + import Param - private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) } + /* Begin: Stage logic. */ + bindingset[result, apa] + private ApApprox unbindApa(ApApprox apa) { + pragma[only_bind_out](apa) = pragma[only_bind_out](result) + } - bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, + Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, + pragma[only_bind_into](config)) and + matchesCall(ccc, call) + } - pragma[inline] - private Content getHeadContent(Ap ap) { exists(result) and ap = true } + /** + * Holds if `node` is reachable with access path `ap` from a source in the + * configuration `config`. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. + */ + pragma[nomagic] + predicate fwdFlow( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + fwdFlow0(node, state, cc, argAp, ap, config) and + PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and + filter(node, state, ap, config) + } - class ApOption = BooleanOption; + pragma[nomagic] + private predicate fwdFlow0( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + sourceNode(node, state, config) and + (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and + argAp = apNone() and + ap = getApNil(node) + or + exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | + fwdFlow(mid, state0, cc, argAp, ap0, config) and + localCc = getLocalCc(mid, cc) + | + localStep(mid, state0, node, state, true, _, config, localCc) and + ap = ap0 + or + localStep(mid, state0, node, state, false, ap, config, localCc) and + ap0 instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + jumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStateStep(mid, state0, node, state, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + // store + exists(TypedContent tc, Ap ap0 | + fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + ap = apCons(tc, ap0) + ) + or + // read + exists(Ap ap0, Content c | + fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowConsCand(ap0, c, ap, config) + ) + or + // flow into a callable + exists(ApApprox apa | + fwdFlowIn(_, node, state, _, cc, _, ap, config) and + apa = getApprox(ap) and + if PrevStage::parameterMayFlowThrough(node, _, apa, config) + then argAp = apSome(ap) + else argAp = apNone() + ) + or + // flow out of a callable + fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) + ) + } - ApOption apNone() { result = TBooleanNone() } + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + exists(DataFlowType contentType | + fwdFlow(node1, state, cc, argAp, ap1, config) and + PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and + typecheckStore(ap1, contentType) + ) + } - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + /** + * Holds if forward flow with access path `tail` reaches a store of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(TypedContent tc | + fwdFlowStore(_, tail, tc, _, _, _, _, config) and + tc.getContent() = c and + cons = apCons(tc, tail) + ) + } + pragma[nomagic] + private predicate fwdFlowRead( + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + fwdFlow(node1, state, cc, argAp, ap, config) and + PrevStage::readStepCand(node1, c, node2, config) and + getHeadContent(ap) = c + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, + Ap ap, Configuration config + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, state, outercc, argAp, ap, config) and + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutNotFromArg( + NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + ) { + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, argAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + ccOut = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + ) + } + + pragma[nomagic] + private predicate storeStepFwd( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config + ) { + fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + ap2 = apCons(tc, ap1) and + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + } + + private predicate readStepFwd( + NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config + ) { + fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowConsCand(ap1, c, ap2, config) + } + + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and + fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), + pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + + pragma[nomagic] + private predicate returnNodeMayFlowThrough( + RetNodeEx ret, FlowState state, Ap ap, Configuration config + ) { + fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from the + * enclosing callable in order to reach a sink, and if so, `returnAp` records + * the access path of the returned value. + */ + pragma[nomagic] + predicate revFlow( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + revFlow0(node, state, toReturn, returnAp, ap, config) and + fwdFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + fwdFlow(node, state, _, _, ap, config) and + sinkNode(node, state, config) and + (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid, FlowState state0 | + localStep(node, state, mid, state0, true, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, ap, config) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, state, _, _, ap, config) and + toReturn = false and + returnAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStep(node, mid, config) and + revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStateStep(node, state, mid, state0, config) and + revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, + pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowConsCand(ap0, c, ap, config) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, state, toReturn, returnAp, ap0, config) and + readStepFwd(node, ap, _, mid, ap0, config) + ) + or + // flow into a callable + revFlowInNotToReturn(node, state, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + or + // flow out of a callable + revFlowOut(_, node, state, _, _, ap, config) and + toReturn = true and + if returnNodeMayFlowThrough(node, state, ap, config) + then returnAp = apSome(ap) + else returnAp = apNone() + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, + boolean toReturn, ApOption returnAp, Configuration config + ) { + revFlow(mid, state, toReturn, returnAp, ap0, config) and + storeStepFwd(node, ap, tc, mid, ap0, config) and + tc.getContent() = c + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, _, tail, config) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0, config) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, + Configuration config + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, state, toReturn, returnAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInNotToReturn( + ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, FlowState state, CcCall ccc | + revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and + fwdFlow(ret, state, ccc, apSome(_), ap, config) and + matchesCall(ccc, call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Ap ap2, Content c | + PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and + revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and + revFlowConsCand(ap2, c, ap1, config) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and + readStepFwd(node1, ap1, c, node2, ap2, config) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, + pragma[only_bind_into](config)) + ) + } + + predicate revFlow(NodeEx node, FlowState state, Configuration config) { + revFlow(node, state, _, _, _, config) + } + + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, ap, config) + } + + private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepFwd(_, ap, tc, _, _, config) + } + + private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepCand(_, ap, tc, _, _, config) + } + + private predicate validAp(Ap ap, Configuration config) { + revFlow(_, _, _, _, ap, config) and ap instanceof ApNil + or + exists(TypedContent head, Ap tail | + consCand(head, tail, config) and + ap = apCons(head, tail) + ) + } + + predicate consCand(TypedContent tc, Ap ap, Configuration config) { + revConsCand(tc, ap, config) and + validAp(ap, config) + } + + pragma[noinline] + private predicate parameterFlow( + ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ) { + revFlow(p, _, true, apSome(ap0), ap, config) and + c = p.getEnclosingCallable() + } + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | + parameterFlow(p, ap, ap0, c, config) and + c = ret.getEnclosingCallable() and + revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), + pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and + fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and + kind = ret.getKind() and + p.getPosition() = pos and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists( + Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + | + revFlow(arg, state, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + + predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, argAp, ap, config) + ) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | consCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and + states = count(FlowState state | revFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | + revFlow(n, state, b, retAp, ap, config) + ) + } + /* End: Stage logic. */ + } +} + +private module BooleanCallContext { + class Cc extends boolean { + Cc() { this in [true, false] } + } + + class CcCall extends Cc { + CcCall() { this = true } + } + + /** Holds if the call context may be `call`. */ + predicate matchesCall(CcCall cc, DataFlowCall call) { any() } + + class CcNoCall extends Cc { + CcNoCall() { this = false } + } + + Cc ccNone() { result = false } + + CcCall ccSomeCall() { result = true } + + class LocalCc = Unit; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } +} + +private module Level1CallContext { class Cc = CallContext; class CcCall = CallContextCall; + pragma[inline] + predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } + class CcNoCall = CallContextNoCall; Cc ccNone() { result instanceof CallContextAny } CcCall ccSomeCall() { result instanceof CallContextSomeCall } - private class LocalCc = Unit; + module NoLocalCallContext { + class LocalCc = Unit; - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSiteDispatch(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSiteDispatch(call, c) + then result = TSpecificCall(call) + else result = TSomeCall() + } + } + + module LocalCallContext { + class LocalCc = LocalCallContext; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { + result = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + node.getEnclosingCallable()) + } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() + } } bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { checkCallContextReturn(innercc, c, call) and if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() } +} - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } +private module Stage2Param implements MkStage::StageParam { + private module PrevStage = Stage1; + + class Ap extends boolean { + Ap() { this in [true, false] } + } + + class ApNil extends Ap { + ApNil() { this = false } + } + + bindingset[result, ap] + PrevStage::Ap getApprox(Ap ap) { any() } + + ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + + pragma[inline] + Content getHeadContent(Ap ap) { exists(result) and ap = true } + + class ApOption = BooleanOption; + + ApOption apNone() { result = TBooleanNone() } + + ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + + import Level1CallContext + import NoLocalCallContext bindingset[node1, state1, config] bindingset[node2, state2, config] - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -1221,9 +1906,9 @@ private module Stage2 { exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/5; - private predicate flowIntoCall = flowIntoCallNodeCand1/5; + predicate flowIntoCall = flowIntoCallNodeCand1/5; pragma[nomagic] private predicate expectsContentCand(NodeEx node, Configuration config) { @@ -1235,7 +1920,7 @@ private module Stage2 { } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and not stateBarrier(node, state, config) and @@ -1248,544 +1933,11 @@ private module Stage2 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 2 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 2 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage2 = MkStage::Stage; + pragma[nomagic] private predicate flowOutOfCallNodeCand2( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config @@ -1883,14 +2035,13 @@ private module LocalFlowBigStep { ) { additionalLocalFlowStepNodeCand1(node1, node2, config) and state1 = state2 and - Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false, - pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false, + Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false, pragma[only_bind_into](config)) or additionalLocalStateStep(node1, state1, node2, state2, config) and - Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config)) + Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config)) } /** @@ -1967,26 +2118,24 @@ private module LocalFlowBigStep { private import LocalFlowBigStep -private module Stage3 { - module PrevStage = Stage2; - - class ApApprox = PrevStage::Ap; +private module Stage3Param implements MkStage::StageParam { + private module PrevStage = Stage2; class Ap = AccessPathFront; class ApNil = AccessPathFrontNil; - private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } + PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } + Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathFrontOption; @@ -1994,44 +2143,18 @@ private module Stage3 { ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - class Cc = boolean; + import BooleanCallContext - class CcCall extends Cc { - CcCall() { this = true } - - /** Holds if this call context may be `call`. */ - predicate matchesCall(DataFlowCall call) { any() } - } - - class CcNoCall extends Cc { - CcNoCall() { this = false } - } - - Cc ccNone() { result = false } - - CcCall ccSomeCall() { result = true } - - private class LocalCc = Unit; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/5; - private predicate flowIntoCall = flowIntoCallNodeCand2/5; + predicate flowIntoCall = flowIntoCallNodeCand2/5; pragma[nomagic] private predicate clearSet(NodeEx node, ContentSet c, Configuration config) { @@ -2067,7 +2190,7 @@ private module Stage3 { private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { exists(state) and exists(config) and not clear(node, ap, config) and @@ -2080,548 +2203,15 @@ private module Stage3 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { + predicate typecheckStore(Ap ap, DataFlowType contentType) { // We need to typecheck stores here, since reverse flow through a getter // might have a different type here compared to inside the getter. compatibleTypes(ap.getType(), contentType) } - - /* Begin: Stage 3 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 3 logic. */ } +private module Stage3 = MkStage::Stage; + /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` * and remains relevant for the following pruning stage. @@ -2644,7 +2234,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and nodes = strictcount(NodeEx n, FlowState state | - Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) or flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) ) and @@ -2828,26 +2418,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } } -private module Stage4 { - module PrevStage = Stage3; - - class ApApprox = PrevStage::Ap; +private module Stage4Param implements MkStage::StageParam { + private module PrevStage = Stage3; class Ap = AccessPathApprox; class ApNil = AccessPathApproxNil; - private ApApprox getApprox(Ap ap) { result = ap.getFront() } + PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } + Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathApproxOption; @@ -2855,38 +2443,10 @@ private module Stage4 { ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - class Cc = CallContext; + import Level1CallContext + import LocalCallContext - class CcCall = CallContextCall; - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - private class LocalCc = LocalCallContext; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() - } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { - result = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - node.getEnclosingCallable()) - } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -2894,575 +2454,40 @@ private module Stage4 { } pragma[nomagic] - private predicate flowOutOfCall( + predicate flowOutOfCall( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } pragma[nomagic] - private predicate flowIntoCall( + predicate flowIntoCall( DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } // Type checking is not necessary here as it has already been done in stage 3. bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 4 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 4 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage4 = MkStage::Stage; + bindingset[conf, result] private Configuration unbindConf(Configuration conf) { exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) @@ -3495,7 +2520,7 @@ private newtype TSummaryCtx = TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and - Stage4::revFlow(p, state, _, _, _, config) + Stage4::revFlow(p, state, _, config) ) } @@ -3553,7 +2578,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { result = strictcount(NodeEx n, FlowState state | - Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config) + Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config) ) } @@ -3667,7 +2692,7 @@ private newtype TPathNode = exists(PathNodeMid mid | pathStep(mid, node, state, cc, sc, ap) and pragma[only_bind_into](config) = mid.getConfiguration() and - Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config)) + Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config)) ) } or TPathNodeSink(NodeEx node, FlowState state, Configuration config) { @@ -4207,7 +3232,7 @@ private NodeEx getAnOutNodeFlow( ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result.asNode() = kind.getAnOutNode(call) and - Stage4::revFlow(result, _, _, _, apa, config) + Stage4::revFlow(result, _, apa, config) } /** @@ -4243,7 +3268,7 @@ private predicate parameterCand( DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config ) { exists(ParamNodeEx p | - Stage4::revFlow(p, _, _, _, apa, config) and + Stage4::revFlow(p, _, apa, config) and p.isParameterOf(callable, pos) ) } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll index a076f7a2e45..22c3bd2466e 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,12 +944,9 @@ private module Stage1 { predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) } bindingset[node, state, config] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, toReturn, pragma[only_bind_into](config)) and + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, _, pragma[only_bind_into](config)) and exists(state) and - exists(returnAp) and exists(ap) } @@ -1142,66 +1139,754 @@ private predicate flowIntoCallNodeCand1( ) } -private module Stage2 { - module PrevStage = Stage1; +private signature module StageSig { + class Ap; + predicate revFlow(NodeEx node, Configuration config); + + bindingset[node, state, config] + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config); + + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ); + + predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config); +} + +private module MkStage { class ApApprox = PrevStage::Ap; - class Ap = boolean; + signature module StageParam { + class Ap; - class ApNil extends Ap { - ApNil() { this = false } + class ApNil extends Ap; + + bindingset[result, ap] + ApApprox getApprox(Ap ap); + + ApNil getApNil(NodeEx node); + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail); + + Content getHeadContent(Ap ap); + + class ApOption; + + ApOption apNone(); + + ApOption apSome(Ap ap); + + class Cc; + + class CcCall extends Cc; + + // TODO: member predicate on CcCall + predicate matchesCall(CcCall cc, DataFlowCall call); + + class CcNoCall extends Cc; + + Cc ccNone(); + + CcCall ccSomeCall(); + + class LocalCc; + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc); + + bindingset[node1, state1, config] + bindingset[node2, state2, config] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ); + + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + ); + + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ); + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config); + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType); } - bindingset[result, ap] - private ApApprox getApprox(Ap ap) { any() } + module Stage implements StageSig { + import Param - private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) } + /* Begin: Stage logic. */ + bindingset[result, apa] + private ApApprox unbindApa(ApApprox apa) { + pragma[only_bind_out](apa) = pragma[only_bind_out](result) + } - bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, + Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, + pragma[only_bind_into](config)) and + matchesCall(ccc, call) + } - pragma[inline] - private Content getHeadContent(Ap ap) { exists(result) and ap = true } + /** + * Holds if `node` is reachable with access path `ap` from a source in the + * configuration `config`. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. + */ + pragma[nomagic] + predicate fwdFlow( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + fwdFlow0(node, state, cc, argAp, ap, config) and + PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and + filter(node, state, ap, config) + } - class ApOption = BooleanOption; + pragma[nomagic] + private predicate fwdFlow0( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + sourceNode(node, state, config) and + (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and + argAp = apNone() and + ap = getApNil(node) + or + exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | + fwdFlow(mid, state0, cc, argAp, ap0, config) and + localCc = getLocalCc(mid, cc) + | + localStep(mid, state0, node, state, true, _, config, localCc) and + ap = ap0 + or + localStep(mid, state0, node, state, false, ap, config, localCc) and + ap0 instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + jumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStateStep(mid, state0, node, state, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + // store + exists(TypedContent tc, Ap ap0 | + fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + ap = apCons(tc, ap0) + ) + or + // read + exists(Ap ap0, Content c | + fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowConsCand(ap0, c, ap, config) + ) + or + // flow into a callable + exists(ApApprox apa | + fwdFlowIn(_, node, state, _, cc, _, ap, config) and + apa = getApprox(ap) and + if PrevStage::parameterMayFlowThrough(node, _, apa, config) + then argAp = apSome(ap) + else argAp = apNone() + ) + or + // flow out of a callable + fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) + ) + } - ApOption apNone() { result = TBooleanNone() } + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + exists(DataFlowType contentType | + fwdFlow(node1, state, cc, argAp, ap1, config) and + PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and + typecheckStore(ap1, contentType) + ) + } - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + /** + * Holds if forward flow with access path `tail` reaches a store of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(TypedContent tc | + fwdFlowStore(_, tail, tc, _, _, _, _, config) and + tc.getContent() = c and + cons = apCons(tc, tail) + ) + } + pragma[nomagic] + private predicate fwdFlowRead( + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + fwdFlow(node1, state, cc, argAp, ap, config) and + PrevStage::readStepCand(node1, c, node2, config) and + getHeadContent(ap) = c + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, + Ap ap, Configuration config + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, state, outercc, argAp, ap, config) and + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutNotFromArg( + NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + ) { + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, argAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + ccOut = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + ) + } + + pragma[nomagic] + private predicate storeStepFwd( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config + ) { + fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + ap2 = apCons(tc, ap1) and + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + } + + private predicate readStepFwd( + NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config + ) { + fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowConsCand(ap1, c, ap2, config) + } + + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and + fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), + pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + + pragma[nomagic] + private predicate returnNodeMayFlowThrough( + RetNodeEx ret, FlowState state, Ap ap, Configuration config + ) { + fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from the + * enclosing callable in order to reach a sink, and if so, `returnAp` records + * the access path of the returned value. + */ + pragma[nomagic] + predicate revFlow( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + revFlow0(node, state, toReturn, returnAp, ap, config) and + fwdFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + fwdFlow(node, state, _, _, ap, config) and + sinkNode(node, state, config) and + (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid, FlowState state0 | + localStep(node, state, mid, state0, true, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, ap, config) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, state, _, _, ap, config) and + toReturn = false and + returnAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStep(node, mid, config) and + revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStateStep(node, state, mid, state0, config) and + revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, + pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowConsCand(ap0, c, ap, config) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, state, toReturn, returnAp, ap0, config) and + readStepFwd(node, ap, _, mid, ap0, config) + ) + or + // flow into a callable + revFlowInNotToReturn(node, state, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + or + // flow out of a callable + revFlowOut(_, node, state, _, _, ap, config) and + toReturn = true and + if returnNodeMayFlowThrough(node, state, ap, config) + then returnAp = apSome(ap) + else returnAp = apNone() + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, + boolean toReturn, ApOption returnAp, Configuration config + ) { + revFlow(mid, state, toReturn, returnAp, ap0, config) and + storeStepFwd(node, ap, tc, mid, ap0, config) and + tc.getContent() = c + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, _, tail, config) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0, config) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, + Configuration config + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, state, toReturn, returnAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInNotToReturn( + ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, FlowState state, CcCall ccc | + revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and + fwdFlow(ret, state, ccc, apSome(_), ap, config) and + matchesCall(ccc, call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Ap ap2, Content c | + PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and + revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and + revFlowConsCand(ap2, c, ap1, config) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and + readStepFwd(node1, ap1, c, node2, ap2, config) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, + pragma[only_bind_into](config)) + ) + } + + predicate revFlow(NodeEx node, FlowState state, Configuration config) { + revFlow(node, state, _, _, _, config) + } + + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, ap, config) + } + + private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepFwd(_, ap, tc, _, _, config) + } + + private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepCand(_, ap, tc, _, _, config) + } + + private predicate validAp(Ap ap, Configuration config) { + revFlow(_, _, _, _, ap, config) and ap instanceof ApNil + or + exists(TypedContent head, Ap tail | + consCand(head, tail, config) and + ap = apCons(head, tail) + ) + } + + predicate consCand(TypedContent tc, Ap ap, Configuration config) { + revConsCand(tc, ap, config) and + validAp(ap, config) + } + + pragma[noinline] + private predicate parameterFlow( + ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ) { + revFlow(p, _, true, apSome(ap0), ap, config) and + c = p.getEnclosingCallable() + } + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | + parameterFlow(p, ap, ap0, c, config) and + c = ret.getEnclosingCallable() and + revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), + pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and + fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and + kind = ret.getKind() and + p.getPosition() = pos and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists( + Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + | + revFlow(arg, state, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + + predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, argAp, ap, config) + ) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | consCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and + states = count(FlowState state | revFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | + revFlow(n, state, b, retAp, ap, config) + ) + } + /* End: Stage logic. */ + } +} + +private module BooleanCallContext { + class Cc extends boolean { + Cc() { this in [true, false] } + } + + class CcCall extends Cc { + CcCall() { this = true } + } + + /** Holds if the call context may be `call`. */ + predicate matchesCall(CcCall cc, DataFlowCall call) { any() } + + class CcNoCall extends Cc { + CcNoCall() { this = false } + } + + Cc ccNone() { result = false } + + CcCall ccSomeCall() { result = true } + + class LocalCc = Unit; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } +} + +private module Level1CallContext { class Cc = CallContext; class CcCall = CallContextCall; + pragma[inline] + predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } + class CcNoCall = CallContextNoCall; Cc ccNone() { result instanceof CallContextAny } CcCall ccSomeCall() { result instanceof CallContextSomeCall } - private class LocalCc = Unit; + module NoLocalCallContext { + class LocalCc = Unit; - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSiteDispatch(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSiteDispatch(call, c) + then result = TSpecificCall(call) + else result = TSomeCall() + } + } + + module LocalCallContext { + class LocalCc = LocalCallContext; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { + result = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + node.getEnclosingCallable()) + } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() + } } bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { checkCallContextReturn(innercc, c, call) and if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() } +} - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } +private module Stage2Param implements MkStage::StageParam { + private module PrevStage = Stage1; + + class Ap extends boolean { + Ap() { this in [true, false] } + } + + class ApNil extends Ap { + ApNil() { this = false } + } + + bindingset[result, ap] + PrevStage::Ap getApprox(Ap ap) { any() } + + ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + + pragma[inline] + Content getHeadContent(Ap ap) { exists(result) and ap = true } + + class ApOption = BooleanOption; + + ApOption apNone() { result = TBooleanNone() } + + ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + + import Level1CallContext + import NoLocalCallContext bindingset[node1, state1, config] bindingset[node2, state2, config] - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -1221,9 +1906,9 @@ private module Stage2 { exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/5; - private predicate flowIntoCall = flowIntoCallNodeCand1/5; + predicate flowIntoCall = flowIntoCallNodeCand1/5; pragma[nomagic] private predicate expectsContentCand(NodeEx node, Configuration config) { @@ -1235,7 +1920,7 @@ private module Stage2 { } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and not stateBarrier(node, state, config) and @@ -1248,544 +1933,11 @@ private module Stage2 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 2 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 2 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage2 = MkStage::Stage; + pragma[nomagic] private predicate flowOutOfCallNodeCand2( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config @@ -1883,14 +2035,13 @@ private module LocalFlowBigStep { ) { additionalLocalFlowStepNodeCand1(node1, node2, config) and state1 = state2 and - Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false, - pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false, + Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false, pragma[only_bind_into](config)) or additionalLocalStateStep(node1, state1, node2, state2, config) and - Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config)) + Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config)) } /** @@ -1967,26 +2118,24 @@ private module LocalFlowBigStep { private import LocalFlowBigStep -private module Stage3 { - module PrevStage = Stage2; - - class ApApprox = PrevStage::Ap; +private module Stage3Param implements MkStage::StageParam { + private module PrevStage = Stage2; class Ap = AccessPathFront; class ApNil = AccessPathFrontNil; - private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } + PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } + Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathFrontOption; @@ -1994,44 +2143,18 @@ private module Stage3 { ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - class Cc = boolean; + import BooleanCallContext - class CcCall extends Cc { - CcCall() { this = true } - - /** Holds if this call context may be `call`. */ - predicate matchesCall(DataFlowCall call) { any() } - } - - class CcNoCall extends Cc { - CcNoCall() { this = false } - } - - Cc ccNone() { result = false } - - CcCall ccSomeCall() { result = true } - - private class LocalCc = Unit; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/5; - private predicate flowIntoCall = flowIntoCallNodeCand2/5; + predicate flowIntoCall = flowIntoCallNodeCand2/5; pragma[nomagic] private predicate clearSet(NodeEx node, ContentSet c, Configuration config) { @@ -2067,7 +2190,7 @@ private module Stage3 { private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { exists(state) and exists(config) and not clear(node, ap, config) and @@ -2080,548 +2203,15 @@ private module Stage3 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { + predicate typecheckStore(Ap ap, DataFlowType contentType) { // We need to typecheck stores here, since reverse flow through a getter // might have a different type here compared to inside the getter. compatibleTypes(ap.getType(), contentType) } - - /* Begin: Stage 3 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 3 logic. */ } +private module Stage3 = MkStage::Stage; + /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` * and remains relevant for the following pruning stage. @@ -2644,7 +2234,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and nodes = strictcount(NodeEx n, FlowState state | - Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) or flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) ) and @@ -2828,26 +2418,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } } -private module Stage4 { - module PrevStage = Stage3; - - class ApApprox = PrevStage::Ap; +private module Stage4Param implements MkStage::StageParam { + private module PrevStage = Stage3; class Ap = AccessPathApprox; class ApNil = AccessPathApproxNil; - private ApApprox getApprox(Ap ap) { result = ap.getFront() } + PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } + Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathApproxOption; @@ -2855,38 +2443,10 @@ private module Stage4 { ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - class Cc = CallContext; + import Level1CallContext + import LocalCallContext - class CcCall = CallContextCall; - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - private class LocalCc = LocalCallContext; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() - } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { - result = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - node.getEnclosingCallable()) - } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -2894,575 +2454,40 @@ private module Stage4 { } pragma[nomagic] - private predicate flowOutOfCall( + predicate flowOutOfCall( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } pragma[nomagic] - private predicate flowIntoCall( + predicate flowIntoCall( DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } // Type checking is not necessary here as it has already been done in stage 3. bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 4 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 4 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage4 = MkStage::Stage; + bindingset[conf, result] private Configuration unbindConf(Configuration conf) { exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) @@ -3495,7 +2520,7 @@ private newtype TSummaryCtx = TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and - Stage4::revFlow(p, state, _, _, _, config) + Stage4::revFlow(p, state, _, config) ) } @@ -3553,7 +2578,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { result = strictcount(NodeEx n, FlowState state | - Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config) + Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config) ) } @@ -3667,7 +2692,7 @@ private newtype TPathNode = exists(PathNodeMid mid | pathStep(mid, node, state, cc, sc, ap) and pragma[only_bind_into](config) = mid.getConfiguration() and - Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config)) + Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config)) ) } or TPathNodeSink(NodeEx node, FlowState state, Configuration config) { @@ -4207,7 +3232,7 @@ private NodeEx getAnOutNodeFlow( ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result.asNode() = kind.getAnOutNode(call) and - Stage4::revFlow(result, _, _, _, apa, config) + Stage4::revFlow(result, _, apa, config) } /** @@ -4243,7 +3268,7 @@ private predicate parameterCand( DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config ) { exists(ParamNodeEx p | - Stage4::revFlow(p, _, _, _, apa, config) and + Stage4::revFlow(p, _, apa, config) and p.isParameterOf(callable, pos) ) } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll index a076f7a2e45..22c3bd2466e 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,12 +944,9 @@ private module Stage1 { predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) } bindingset[node, state, config] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, toReturn, pragma[only_bind_into](config)) and + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, _, pragma[only_bind_into](config)) and exists(state) and - exists(returnAp) and exists(ap) } @@ -1142,66 +1139,754 @@ private predicate flowIntoCallNodeCand1( ) } -private module Stage2 { - module PrevStage = Stage1; +private signature module StageSig { + class Ap; + predicate revFlow(NodeEx node, Configuration config); + + bindingset[node, state, config] + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config); + + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ); + + predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config); +} + +private module MkStage { class ApApprox = PrevStage::Ap; - class Ap = boolean; + signature module StageParam { + class Ap; - class ApNil extends Ap { - ApNil() { this = false } + class ApNil extends Ap; + + bindingset[result, ap] + ApApprox getApprox(Ap ap); + + ApNil getApNil(NodeEx node); + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail); + + Content getHeadContent(Ap ap); + + class ApOption; + + ApOption apNone(); + + ApOption apSome(Ap ap); + + class Cc; + + class CcCall extends Cc; + + // TODO: member predicate on CcCall + predicate matchesCall(CcCall cc, DataFlowCall call); + + class CcNoCall extends Cc; + + Cc ccNone(); + + CcCall ccSomeCall(); + + class LocalCc; + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc); + + bindingset[node1, state1, config] + bindingset[node2, state2, config] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ); + + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + ); + + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ); + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config); + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType); } - bindingset[result, ap] - private ApApprox getApprox(Ap ap) { any() } + module Stage implements StageSig { + import Param - private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) } + /* Begin: Stage logic. */ + bindingset[result, apa] + private ApApprox unbindApa(ApApprox apa) { + pragma[only_bind_out](apa) = pragma[only_bind_out](result) + } - bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, + Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, + pragma[only_bind_into](config)) and + matchesCall(ccc, call) + } - pragma[inline] - private Content getHeadContent(Ap ap) { exists(result) and ap = true } + /** + * Holds if `node` is reachable with access path `ap` from a source in the + * configuration `config`. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. + */ + pragma[nomagic] + predicate fwdFlow( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + fwdFlow0(node, state, cc, argAp, ap, config) and + PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and + filter(node, state, ap, config) + } - class ApOption = BooleanOption; + pragma[nomagic] + private predicate fwdFlow0( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + sourceNode(node, state, config) and + (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and + argAp = apNone() and + ap = getApNil(node) + or + exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | + fwdFlow(mid, state0, cc, argAp, ap0, config) and + localCc = getLocalCc(mid, cc) + | + localStep(mid, state0, node, state, true, _, config, localCc) and + ap = ap0 + or + localStep(mid, state0, node, state, false, ap, config, localCc) and + ap0 instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + jumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStateStep(mid, state0, node, state, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + // store + exists(TypedContent tc, Ap ap0 | + fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + ap = apCons(tc, ap0) + ) + or + // read + exists(Ap ap0, Content c | + fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowConsCand(ap0, c, ap, config) + ) + or + // flow into a callable + exists(ApApprox apa | + fwdFlowIn(_, node, state, _, cc, _, ap, config) and + apa = getApprox(ap) and + if PrevStage::parameterMayFlowThrough(node, _, apa, config) + then argAp = apSome(ap) + else argAp = apNone() + ) + or + // flow out of a callable + fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) + ) + } - ApOption apNone() { result = TBooleanNone() } + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + exists(DataFlowType contentType | + fwdFlow(node1, state, cc, argAp, ap1, config) and + PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and + typecheckStore(ap1, contentType) + ) + } - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + /** + * Holds if forward flow with access path `tail` reaches a store of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(TypedContent tc | + fwdFlowStore(_, tail, tc, _, _, _, _, config) and + tc.getContent() = c and + cons = apCons(tc, tail) + ) + } + pragma[nomagic] + private predicate fwdFlowRead( + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + fwdFlow(node1, state, cc, argAp, ap, config) and + PrevStage::readStepCand(node1, c, node2, config) and + getHeadContent(ap) = c + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, + Ap ap, Configuration config + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, state, outercc, argAp, ap, config) and + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutNotFromArg( + NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + ) { + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, argAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + ccOut = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + ) + } + + pragma[nomagic] + private predicate storeStepFwd( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config + ) { + fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + ap2 = apCons(tc, ap1) and + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + } + + private predicate readStepFwd( + NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config + ) { + fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowConsCand(ap1, c, ap2, config) + } + + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and + fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), + pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + + pragma[nomagic] + private predicate returnNodeMayFlowThrough( + RetNodeEx ret, FlowState state, Ap ap, Configuration config + ) { + fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from the + * enclosing callable in order to reach a sink, and if so, `returnAp` records + * the access path of the returned value. + */ + pragma[nomagic] + predicate revFlow( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + revFlow0(node, state, toReturn, returnAp, ap, config) and + fwdFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + fwdFlow(node, state, _, _, ap, config) and + sinkNode(node, state, config) and + (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid, FlowState state0 | + localStep(node, state, mid, state0, true, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, ap, config) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, state, _, _, ap, config) and + toReturn = false and + returnAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStep(node, mid, config) and + revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStateStep(node, state, mid, state0, config) and + revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, + pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowConsCand(ap0, c, ap, config) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, state, toReturn, returnAp, ap0, config) and + readStepFwd(node, ap, _, mid, ap0, config) + ) + or + // flow into a callable + revFlowInNotToReturn(node, state, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + or + // flow out of a callable + revFlowOut(_, node, state, _, _, ap, config) and + toReturn = true and + if returnNodeMayFlowThrough(node, state, ap, config) + then returnAp = apSome(ap) + else returnAp = apNone() + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, + boolean toReturn, ApOption returnAp, Configuration config + ) { + revFlow(mid, state, toReturn, returnAp, ap0, config) and + storeStepFwd(node, ap, tc, mid, ap0, config) and + tc.getContent() = c + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, _, tail, config) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0, config) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, + Configuration config + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, state, toReturn, returnAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInNotToReturn( + ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, FlowState state, CcCall ccc | + revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and + fwdFlow(ret, state, ccc, apSome(_), ap, config) and + matchesCall(ccc, call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Ap ap2, Content c | + PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and + revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and + revFlowConsCand(ap2, c, ap1, config) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and + readStepFwd(node1, ap1, c, node2, ap2, config) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, + pragma[only_bind_into](config)) + ) + } + + predicate revFlow(NodeEx node, FlowState state, Configuration config) { + revFlow(node, state, _, _, _, config) + } + + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, ap, config) + } + + private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepFwd(_, ap, tc, _, _, config) + } + + private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepCand(_, ap, tc, _, _, config) + } + + private predicate validAp(Ap ap, Configuration config) { + revFlow(_, _, _, _, ap, config) and ap instanceof ApNil + or + exists(TypedContent head, Ap tail | + consCand(head, tail, config) and + ap = apCons(head, tail) + ) + } + + predicate consCand(TypedContent tc, Ap ap, Configuration config) { + revConsCand(tc, ap, config) and + validAp(ap, config) + } + + pragma[noinline] + private predicate parameterFlow( + ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ) { + revFlow(p, _, true, apSome(ap0), ap, config) and + c = p.getEnclosingCallable() + } + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | + parameterFlow(p, ap, ap0, c, config) and + c = ret.getEnclosingCallable() and + revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), + pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and + fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and + kind = ret.getKind() and + p.getPosition() = pos and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists( + Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + | + revFlow(arg, state, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + + predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, argAp, ap, config) + ) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | consCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and + states = count(FlowState state | revFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | + revFlow(n, state, b, retAp, ap, config) + ) + } + /* End: Stage logic. */ + } +} + +private module BooleanCallContext { + class Cc extends boolean { + Cc() { this in [true, false] } + } + + class CcCall extends Cc { + CcCall() { this = true } + } + + /** Holds if the call context may be `call`. */ + predicate matchesCall(CcCall cc, DataFlowCall call) { any() } + + class CcNoCall extends Cc { + CcNoCall() { this = false } + } + + Cc ccNone() { result = false } + + CcCall ccSomeCall() { result = true } + + class LocalCc = Unit; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } +} + +private module Level1CallContext { class Cc = CallContext; class CcCall = CallContextCall; + pragma[inline] + predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } + class CcNoCall = CallContextNoCall; Cc ccNone() { result instanceof CallContextAny } CcCall ccSomeCall() { result instanceof CallContextSomeCall } - private class LocalCc = Unit; + module NoLocalCallContext { + class LocalCc = Unit; - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSiteDispatch(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSiteDispatch(call, c) + then result = TSpecificCall(call) + else result = TSomeCall() + } + } + + module LocalCallContext { + class LocalCc = LocalCallContext; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { + result = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + node.getEnclosingCallable()) + } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() + } } bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { checkCallContextReturn(innercc, c, call) and if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() } +} - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } +private module Stage2Param implements MkStage::StageParam { + private module PrevStage = Stage1; + + class Ap extends boolean { + Ap() { this in [true, false] } + } + + class ApNil extends Ap { + ApNil() { this = false } + } + + bindingset[result, ap] + PrevStage::Ap getApprox(Ap ap) { any() } + + ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + + pragma[inline] + Content getHeadContent(Ap ap) { exists(result) and ap = true } + + class ApOption = BooleanOption; + + ApOption apNone() { result = TBooleanNone() } + + ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + + import Level1CallContext + import NoLocalCallContext bindingset[node1, state1, config] bindingset[node2, state2, config] - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -1221,9 +1906,9 @@ private module Stage2 { exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/5; - private predicate flowIntoCall = flowIntoCallNodeCand1/5; + predicate flowIntoCall = flowIntoCallNodeCand1/5; pragma[nomagic] private predicate expectsContentCand(NodeEx node, Configuration config) { @@ -1235,7 +1920,7 @@ private module Stage2 { } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and not stateBarrier(node, state, config) and @@ -1248,544 +1933,11 @@ private module Stage2 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 2 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 2 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage2 = MkStage::Stage; + pragma[nomagic] private predicate flowOutOfCallNodeCand2( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config @@ -1883,14 +2035,13 @@ private module LocalFlowBigStep { ) { additionalLocalFlowStepNodeCand1(node1, node2, config) and state1 = state2 and - Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false, - pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false, + Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false, pragma[only_bind_into](config)) or additionalLocalStateStep(node1, state1, node2, state2, config) and - Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config)) + Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config)) } /** @@ -1967,26 +2118,24 @@ private module LocalFlowBigStep { private import LocalFlowBigStep -private module Stage3 { - module PrevStage = Stage2; - - class ApApprox = PrevStage::Ap; +private module Stage3Param implements MkStage::StageParam { + private module PrevStage = Stage2; class Ap = AccessPathFront; class ApNil = AccessPathFrontNil; - private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } + PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } + Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathFrontOption; @@ -1994,44 +2143,18 @@ private module Stage3 { ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - class Cc = boolean; + import BooleanCallContext - class CcCall extends Cc { - CcCall() { this = true } - - /** Holds if this call context may be `call`. */ - predicate matchesCall(DataFlowCall call) { any() } - } - - class CcNoCall extends Cc { - CcNoCall() { this = false } - } - - Cc ccNone() { result = false } - - CcCall ccSomeCall() { result = true } - - private class LocalCc = Unit; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/5; - private predicate flowIntoCall = flowIntoCallNodeCand2/5; + predicate flowIntoCall = flowIntoCallNodeCand2/5; pragma[nomagic] private predicate clearSet(NodeEx node, ContentSet c, Configuration config) { @@ -2067,7 +2190,7 @@ private module Stage3 { private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { exists(state) and exists(config) and not clear(node, ap, config) and @@ -2080,548 +2203,15 @@ private module Stage3 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { + predicate typecheckStore(Ap ap, DataFlowType contentType) { // We need to typecheck stores here, since reverse flow through a getter // might have a different type here compared to inside the getter. compatibleTypes(ap.getType(), contentType) } - - /* Begin: Stage 3 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 3 logic. */ } +private module Stage3 = MkStage::Stage; + /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` * and remains relevant for the following pruning stage. @@ -2644,7 +2234,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and nodes = strictcount(NodeEx n, FlowState state | - Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) or flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) ) and @@ -2828,26 +2418,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } } -private module Stage4 { - module PrevStage = Stage3; - - class ApApprox = PrevStage::Ap; +private module Stage4Param implements MkStage::StageParam { + private module PrevStage = Stage3; class Ap = AccessPathApprox; class ApNil = AccessPathApproxNil; - private ApApprox getApprox(Ap ap) { result = ap.getFront() } + PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } + Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathApproxOption; @@ -2855,38 +2443,10 @@ private module Stage4 { ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - class Cc = CallContext; + import Level1CallContext + import LocalCallContext - class CcCall = CallContextCall; - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - private class LocalCc = LocalCallContext; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() - } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { - result = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - node.getEnclosingCallable()) - } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -2894,575 +2454,40 @@ private module Stage4 { } pragma[nomagic] - private predicate flowOutOfCall( + predicate flowOutOfCall( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } pragma[nomagic] - private predicate flowIntoCall( + predicate flowIntoCall( DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } // Type checking is not necessary here as it has already been done in stage 3. bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 4 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 4 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage4 = MkStage::Stage; + bindingset[conf, result] private Configuration unbindConf(Configuration conf) { exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) @@ -3495,7 +2520,7 @@ private newtype TSummaryCtx = TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and - Stage4::revFlow(p, state, _, _, _, config) + Stage4::revFlow(p, state, _, config) ) } @@ -3553,7 +2578,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { result = strictcount(NodeEx n, FlowState state | - Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config) + Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config) ) } @@ -3667,7 +2692,7 @@ private newtype TPathNode = exists(PathNodeMid mid | pathStep(mid, node, state, cc, sc, ap) and pragma[only_bind_into](config) = mid.getConfiguration() and - Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config)) + Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config)) ) } or TPathNodeSink(NodeEx node, FlowState state, Configuration config) { @@ -4207,7 +3232,7 @@ private NodeEx getAnOutNodeFlow( ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result.asNode() = kind.getAnOutNode(call) and - Stage4::revFlow(result, _, _, _, apa, config) + Stage4::revFlow(result, _, apa, config) } /** @@ -4243,7 +3268,7 @@ private predicate parameterCand( DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config ) { exists(ParamNodeEx p | - Stage4::revFlow(p, _, _, _, apa, config) and + Stage4::revFlow(p, _, apa, config) and p.isParameterOf(callable, pos) ) } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll index a076f7a2e45..22c3bd2466e 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,12 +944,9 @@ private module Stage1 { predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) } bindingset[node, state, config] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, toReturn, pragma[only_bind_into](config)) and + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, _, pragma[only_bind_into](config)) and exists(state) and - exists(returnAp) and exists(ap) } @@ -1142,66 +1139,754 @@ private predicate flowIntoCallNodeCand1( ) } -private module Stage2 { - module PrevStage = Stage1; +private signature module StageSig { + class Ap; + predicate revFlow(NodeEx node, Configuration config); + + bindingset[node, state, config] + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config); + + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ); + + predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config); +} + +private module MkStage { class ApApprox = PrevStage::Ap; - class Ap = boolean; + signature module StageParam { + class Ap; - class ApNil extends Ap { - ApNil() { this = false } + class ApNil extends Ap; + + bindingset[result, ap] + ApApprox getApprox(Ap ap); + + ApNil getApNil(NodeEx node); + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail); + + Content getHeadContent(Ap ap); + + class ApOption; + + ApOption apNone(); + + ApOption apSome(Ap ap); + + class Cc; + + class CcCall extends Cc; + + // TODO: member predicate on CcCall + predicate matchesCall(CcCall cc, DataFlowCall call); + + class CcNoCall extends Cc; + + Cc ccNone(); + + CcCall ccSomeCall(); + + class LocalCc; + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc); + + bindingset[node1, state1, config] + bindingset[node2, state2, config] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ); + + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + ); + + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ); + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config); + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType); } - bindingset[result, ap] - private ApApprox getApprox(Ap ap) { any() } + module Stage implements StageSig { + import Param - private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) } + /* Begin: Stage logic. */ + bindingset[result, apa] + private ApApprox unbindApa(ApApprox apa) { + pragma[only_bind_out](apa) = pragma[only_bind_out](result) + } - bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, + Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, + pragma[only_bind_into](config)) and + matchesCall(ccc, call) + } - pragma[inline] - private Content getHeadContent(Ap ap) { exists(result) and ap = true } + /** + * Holds if `node` is reachable with access path `ap` from a source in the + * configuration `config`. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. + */ + pragma[nomagic] + predicate fwdFlow( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + fwdFlow0(node, state, cc, argAp, ap, config) and + PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and + filter(node, state, ap, config) + } - class ApOption = BooleanOption; + pragma[nomagic] + private predicate fwdFlow0( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + sourceNode(node, state, config) and + (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and + argAp = apNone() and + ap = getApNil(node) + or + exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | + fwdFlow(mid, state0, cc, argAp, ap0, config) and + localCc = getLocalCc(mid, cc) + | + localStep(mid, state0, node, state, true, _, config, localCc) and + ap = ap0 + or + localStep(mid, state0, node, state, false, ap, config, localCc) and + ap0 instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + jumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStateStep(mid, state0, node, state, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + // store + exists(TypedContent tc, Ap ap0 | + fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + ap = apCons(tc, ap0) + ) + or + // read + exists(Ap ap0, Content c | + fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowConsCand(ap0, c, ap, config) + ) + or + // flow into a callable + exists(ApApprox apa | + fwdFlowIn(_, node, state, _, cc, _, ap, config) and + apa = getApprox(ap) and + if PrevStage::parameterMayFlowThrough(node, _, apa, config) + then argAp = apSome(ap) + else argAp = apNone() + ) + or + // flow out of a callable + fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) + ) + } - ApOption apNone() { result = TBooleanNone() } + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + exists(DataFlowType contentType | + fwdFlow(node1, state, cc, argAp, ap1, config) and + PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and + typecheckStore(ap1, contentType) + ) + } - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + /** + * Holds if forward flow with access path `tail` reaches a store of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(TypedContent tc | + fwdFlowStore(_, tail, tc, _, _, _, _, config) and + tc.getContent() = c and + cons = apCons(tc, tail) + ) + } + pragma[nomagic] + private predicate fwdFlowRead( + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + fwdFlow(node1, state, cc, argAp, ap, config) and + PrevStage::readStepCand(node1, c, node2, config) and + getHeadContent(ap) = c + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, + Ap ap, Configuration config + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, state, outercc, argAp, ap, config) and + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutNotFromArg( + NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + ) { + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, argAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + ccOut = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + ) + } + + pragma[nomagic] + private predicate storeStepFwd( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config + ) { + fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + ap2 = apCons(tc, ap1) and + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + } + + private predicate readStepFwd( + NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config + ) { + fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowConsCand(ap1, c, ap2, config) + } + + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and + fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), + pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + + pragma[nomagic] + private predicate returnNodeMayFlowThrough( + RetNodeEx ret, FlowState state, Ap ap, Configuration config + ) { + fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from the + * enclosing callable in order to reach a sink, and if so, `returnAp` records + * the access path of the returned value. + */ + pragma[nomagic] + predicate revFlow( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + revFlow0(node, state, toReturn, returnAp, ap, config) and + fwdFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + fwdFlow(node, state, _, _, ap, config) and + sinkNode(node, state, config) and + (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid, FlowState state0 | + localStep(node, state, mid, state0, true, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, ap, config) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, state, _, _, ap, config) and + toReturn = false and + returnAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStep(node, mid, config) and + revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStateStep(node, state, mid, state0, config) and + revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, + pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowConsCand(ap0, c, ap, config) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, state, toReturn, returnAp, ap0, config) and + readStepFwd(node, ap, _, mid, ap0, config) + ) + or + // flow into a callable + revFlowInNotToReturn(node, state, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + or + // flow out of a callable + revFlowOut(_, node, state, _, _, ap, config) and + toReturn = true and + if returnNodeMayFlowThrough(node, state, ap, config) + then returnAp = apSome(ap) + else returnAp = apNone() + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, + boolean toReturn, ApOption returnAp, Configuration config + ) { + revFlow(mid, state, toReturn, returnAp, ap0, config) and + storeStepFwd(node, ap, tc, mid, ap0, config) and + tc.getContent() = c + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, _, tail, config) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0, config) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, + Configuration config + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, state, toReturn, returnAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInNotToReturn( + ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, FlowState state, CcCall ccc | + revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and + fwdFlow(ret, state, ccc, apSome(_), ap, config) and + matchesCall(ccc, call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Ap ap2, Content c | + PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and + revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and + revFlowConsCand(ap2, c, ap1, config) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and + readStepFwd(node1, ap1, c, node2, ap2, config) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, + pragma[only_bind_into](config)) + ) + } + + predicate revFlow(NodeEx node, FlowState state, Configuration config) { + revFlow(node, state, _, _, _, config) + } + + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, ap, config) + } + + private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepFwd(_, ap, tc, _, _, config) + } + + private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepCand(_, ap, tc, _, _, config) + } + + private predicate validAp(Ap ap, Configuration config) { + revFlow(_, _, _, _, ap, config) and ap instanceof ApNil + or + exists(TypedContent head, Ap tail | + consCand(head, tail, config) and + ap = apCons(head, tail) + ) + } + + predicate consCand(TypedContent tc, Ap ap, Configuration config) { + revConsCand(tc, ap, config) and + validAp(ap, config) + } + + pragma[noinline] + private predicate parameterFlow( + ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ) { + revFlow(p, _, true, apSome(ap0), ap, config) and + c = p.getEnclosingCallable() + } + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | + parameterFlow(p, ap, ap0, c, config) and + c = ret.getEnclosingCallable() and + revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), + pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and + fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and + kind = ret.getKind() and + p.getPosition() = pos and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists( + Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + | + revFlow(arg, state, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + + predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, argAp, ap, config) + ) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | consCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and + states = count(FlowState state | revFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | + revFlow(n, state, b, retAp, ap, config) + ) + } + /* End: Stage logic. */ + } +} + +private module BooleanCallContext { + class Cc extends boolean { + Cc() { this in [true, false] } + } + + class CcCall extends Cc { + CcCall() { this = true } + } + + /** Holds if the call context may be `call`. */ + predicate matchesCall(CcCall cc, DataFlowCall call) { any() } + + class CcNoCall extends Cc { + CcNoCall() { this = false } + } + + Cc ccNone() { result = false } + + CcCall ccSomeCall() { result = true } + + class LocalCc = Unit; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } +} + +private module Level1CallContext { class Cc = CallContext; class CcCall = CallContextCall; + pragma[inline] + predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } + class CcNoCall = CallContextNoCall; Cc ccNone() { result instanceof CallContextAny } CcCall ccSomeCall() { result instanceof CallContextSomeCall } - private class LocalCc = Unit; + module NoLocalCallContext { + class LocalCc = Unit; - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSiteDispatch(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSiteDispatch(call, c) + then result = TSpecificCall(call) + else result = TSomeCall() + } + } + + module LocalCallContext { + class LocalCc = LocalCallContext; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { + result = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + node.getEnclosingCallable()) + } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() + } } bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { checkCallContextReturn(innercc, c, call) and if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() } +} - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } +private module Stage2Param implements MkStage::StageParam { + private module PrevStage = Stage1; + + class Ap extends boolean { + Ap() { this in [true, false] } + } + + class ApNil extends Ap { + ApNil() { this = false } + } + + bindingset[result, ap] + PrevStage::Ap getApprox(Ap ap) { any() } + + ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + + pragma[inline] + Content getHeadContent(Ap ap) { exists(result) and ap = true } + + class ApOption = BooleanOption; + + ApOption apNone() { result = TBooleanNone() } + + ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + + import Level1CallContext + import NoLocalCallContext bindingset[node1, state1, config] bindingset[node2, state2, config] - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -1221,9 +1906,9 @@ private module Stage2 { exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/5; - private predicate flowIntoCall = flowIntoCallNodeCand1/5; + predicate flowIntoCall = flowIntoCallNodeCand1/5; pragma[nomagic] private predicate expectsContentCand(NodeEx node, Configuration config) { @@ -1235,7 +1920,7 @@ private module Stage2 { } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and not stateBarrier(node, state, config) and @@ -1248,544 +1933,11 @@ private module Stage2 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 2 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 2 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage2 = MkStage::Stage; + pragma[nomagic] private predicate flowOutOfCallNodeCand2( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config @@ -1883,14 +2035,13 @@ private module LocalFlowBigStep { ) { additionalLocalFlowStepNodeCand1(node1, node2, config) and state1 = state2 and - Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false, - pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false, + Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false, pragma[only_bind_into](config)) or additionalLocalStateStep(node1, state1, node2, state2, config) and - Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config)) + Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config)) } /** @@ -1967,26 +2118,24 @@ private module LocalFlowBigStep { private import LocalFlowBigStep -private module Stage3 { - module PrevStage = Stage2; - - class ApApprox = PrevStage::Ap; +private module Stage3Param implements MkStage::StageParam { + private module PrevStage = Stage2; class Ap = AccessPathFront; class ApNil = AccessPathFrontNil; - private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } + PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } + Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathFrontOption; @@ -1994,44 +2143,18 @@ private module Stage3 { ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - class Cc = boolean; + import BooleanCallContext - class CcCall extends Cc { - CcCall() { this = true } - - /** Holds if this call context may be `call`. */ - predicate matchesCall(DataFlowCall call) { any() } - } - - class CcNoCall extends Cc { - CcNoCall() { this = false } - } - - Cc ccNone() { result = false } - - CcCall ccSomeCall() { result = true } - - private class LocalCc = Unit; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/5; - private predicate flowIntoCall = flowIntoCallNodeCand2/5; + predicate flowIntoCall = flowIntoCallNodeCand2/5; pragma[nomagic] private predicate clearSet(NodeEx node, ContentSet c, Configuration config) { @@ -2067,7 +2190,7 @@ private module Stage3 { private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { exists(state) and exists(config) and not clear(node, ap, config) and @@ -2080,548 +2203,15 @@ private module Stage3 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { + predicate typecheckStore(Ap ap, DataFlowType contentType) { // We need to typecheck stores here, since reverse flow through a getter // might have a different type here compared to inside the getter. compatibleTypes(ap.getType(), contentType) } - - /* Begin: Stage 3 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 3 logic. */ } +private module Stage3 = MkStage::Stage; + /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` * and remains relevant for the following pruning stage. @@ -2644,7 +2234,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and nodes = strictcount(NodeEx n, FlowState state | - Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) or flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) ) and @@ -2828,26 +2418,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } } -private module Stage4 { - module PrevStage = Stage3; - - class ApApprox = PrevStage::Ap; +private module Stage4Param implements MkStage::StageParam { + private module PrevStage = Stage3; class Ap = AccessPathApprox; class ApNil = AccessPathApproxNil; - private ApApprox getApprox(Ap ap) { result = ap.getFront() } + PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } + Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathApproxOption; @@ -2855,38 +2443,10 @@ private module Stage4 { ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - class Cc = CallContext; + import Level1CallContext + import LocalCallContext - class CcCall = CallContextCall; - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - private class LocalCc = LocalCallContext; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() - } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { - result = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - node.getEnclosingCallable()) - } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -2894,575 +2454,40 @@ private module Stage4 { } pragma[nomagic] - private predicate flowOutOfCall( + predicate flowOutOfCall( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } pragma[nomagic] - private predicate flowIntoCall( + predicate flowIntoCall( DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } // Type checking is not necessary here as it has already been done in stage 3. bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 4 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 4 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage4 = MkStage::Stage; + bindingset[conf, result] private Configuration unbindConf(Configuration conf) { exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) @@ -3495,7 +2520,7 @@ private newtype TSummaryCtx = TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and - Stage4::revFlow(p, state, _, _, _, config) + Stage4::revFlow(p, state, _, config) ) } @@ -3553,7 +2578,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { result = strictcount(NodeEx n, FlowState state | - Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config) + Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config) ) } @@ -3667,7 +2692,7 @@ private newtype TPathNode = exists(PathNodeMid mid | pathStep(mid, node, state, cc, sc, ap) and pragma[only_bind_into](config) = mid.getConfiguration() and - Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config)) + Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config)) ) } or TPathNodeSink(NodeEx node, FlowState state, Configuration config) { @@ -4207,7 +3232,7 @@ private NodeEx getAnOutNodeFlow( ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result.asNode() = kind.getAnOutNode(call) and - Stage4::revFlow(result, _, _, _, apa, config) + Stage4::revFlow(result, _, apa, config) } /** @@ -4243,7 +3268,7 @@ private predicate parameterCand( DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config ) { exists(ParamNodeEx p | - Stage4::revFlow(p, _, _, _, apa, config) and + Stage4::revFlow(p, _, apa, config) and p.isParameterOf(callable, pos) ) } diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll index a076f7a2e45..22c3bd2466e 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,12 +944,9 @@ private module Stage1 { predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) } bindingset[node, state, config] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, toReturn, pragma[only_bind_into](config)) and + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, _, pragma[only_bind_into](config)) and exists(state) and - exists(returnAp) and exists(ap) } @@ -1142,66 +1139,754 @@ private predicate flowIntoCallNodeCand1( ) } -private module Stage2 { - module PrevStage = Stage1; +private signature module StageSig { + class Ap; + predicate revFlow(NodeEx node, Configuration config); + + bindingset[node, state, config] + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config); + + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ); + + predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config); +} + +private module MkStage { class ApApprox = PrevStage::Ap; - class Ap = boolean; + signature module StageParam { + class Ap; - class ApNil extends Ap { - ApNil() { this = false } + class ApNil extends Ap; + + bindingset[result, ap] + ApApprox getApprox(Ap ap); + + ApNil getApNil(NodeEx node); + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail); + + Content getHeadContent(Ap ap); + + class ApOption; + + ApOption apNone(); + + ApOption apSome(Ap ap); + + class Cc; + + class CcCall extends Cc; + + // TODO: member predicate on CcCall + predicate matchesCall(CcCall cc, DataFlowCall call); + + class CcNoCall extends Cc; + + Cc ccNone(); + + CcCall ccSomeCall(); + + class LocalCc; + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc); + + bindingset[node1, state1, config] + bindingset[node2, state2, config] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ); + + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + ); + + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ); + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config); + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType); } - bindingset[result, ap] - private ApApprox getApprox(Ap ap) { any() } + module Stage implements StageSig { + import Param - private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) } + /* Begin: Stage logic. */ + bindingset[result, apa] + private ApApprox unbindApa(ApApprox apa) { + pragma[only_bind_out](apa) = pragma[only_bind_out](result) + } - bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, + Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, + pragma[only_bind_into](config)) and + matchesCall(ccc, call) + } - pragma[inline] - private Content getHeadContent(Ap ap) { exists(result) and ap = true } + /** + * Holds if `node` is reachable with access path `ap` from a source in the + * configuration `config`. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. + */ + pragma[nomagic] + predicate fwdFlow( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + fwdFlow0(node, state, cc, argAp, ap, config) and + PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and + filter(node, state, ap, config) + } - class ApOption = BooleanOption; + pragma[nomagic] + private predicate fwdFlow0( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + sourceNode(node, state, config) and + (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and + argAp = apNone() and + ap = getApNil(node) + or + exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | + fwdFlow(mid, state0, cc, argAp, ap0, config) and + localCc = getLocalCc(mid, cc) + | + localStep(mid, state0, node, state, true, _, config, localCc) and + ap = ap0 + or + localStep(mid, state0, node, state, false, ap, config, localCc) and + ap0 instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + jumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStateStep(mid, state0, node, state, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + // store + exists(TypedContent tc, Ap ap0 | + fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + ap = apCons(tc, ap0) + ) + or + // read + exists(Ap ap0, Content c | + fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowConsCand(ap0, c, ap, config) + ) + or + // flow into a callable + exists(ApApprox apa | + fwdFlowIn(_, node, state, _, cc, _, ap, config) and + apa = getApprox(ap) and + if PrevStage::parameterMayFlowThrough(node, _, apa, config) + then argAp = apSome(ap) + else argAp = apNone() + ) + or + // flow out of a callable + fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) + ) + } - ApOption apNone() { result = TBooleanNone() } + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + exists(DataFlowType contentType | + fwdFlow(node1, state, cc, argAp, ap1, config) and + PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and + typecheckStore(ap1, contentType) + ) + } - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + /** + * Holds if forward flow with access path `tail` reaches a store of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(TypedContent tc | + fwdFlowStore(_, tail, tc, _, _, _, _, config) and + tc.getContent() = c and + cons = apCons(tc, tail) + ) + } + pragma[nomagic] + private predicate fwdFlowRead( + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + fwdFlow(node1, state, cc, argAp, ap, config) and + PrevStage::readStepCand(node1, c, node2, config) and + getHeadContent(ap) = c + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, + Ap ap, Configuration config + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, state, outercc, argAp, ap, config) and + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutNotFromArg( + NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + ) { + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, argAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + ccOut = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + ) + } + + pragma[nomagic] + private predicate storeStepFwd( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config + ) { + fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + ap2 = apCons(tc, ap1) and + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + } + + private predicate readStepFwd( + NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config + ) { + fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowConsCand(ap1, c, ap2, config) + } + + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and + fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), + pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + + pragma[nomagic] + private predicate returnNodeMayFlowThrough( + RetNodeEx ret, FlowState state, Ap ap, Configuration config + ) { + fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from the + * enclosing callable in order to reach a sink, and if so, `returnAp` records + * the access path of the returned value. + */ + pragma[nomagic] + predicate revFlow( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + revFlow0(node, state, toReturn, returnAp, ap, config) and + fwdFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + fwdFlow(node, state, _, _, ap, config) and + sinkNode(node, state, config) and + (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid, FlowState state0 | + localStep(node, state, mid, state0, true, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, ap, config) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, state, _, _, ap, config) and + toReturn = false and + returnAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStep(node, mid, config) and + revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStateStep(node, state, mid, state0, config) and + revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, + pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowConsCand(ap0, c, ap, config) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, state, toReturn, returnAp, ap0, config) and + readStepFwd(node, ap, _, mid, ap0, config) + ) + or + // flow into a callable + revFlowInNotToReturn(node, state, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + or + // flow out of a callable + revFlowOut(_, node, state, _, _, ap, config) and + toReturn = true and + if returnNodeMayFlowThrough(node, state, ap, config) + then returnAp = apSome(ap) + else returnAp = apNone() + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, + boolean toReturn, ApOption returnAp, Configuration config + ) { + revFlow(mid, state, toReturn, returnAp, ap0, config) and + storeStepFwd(node, ap, tc, mid, ap0, config) and + tc.getContent() = c + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, _, tail, config) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0, config) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, + Configuration config + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, state, toReturn, returnAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInNotToReturn( + ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, FlowState state, CcCall ccc | + revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and + fwdFlow(ret, state, ccc, apSome(_), ap, config) and + matchesCall(ccc, call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Ap ap2, Content c | + PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and + revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and + revFlowConsCand(ap2, c, ap1, config) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and + readStepFwd(node1, ap1, c, node2, ap2, config) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, + pragma[only_bind_into](config)) + ) + } + + predicate revFlow(NodeEx node, FlowState state, Configuration config) { + revFlow(node, state, _, _, _, config) + } + + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, ap, config) + } + + private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepFwd(_, ap, tc, _, _, config) + } + + private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepCand(_, ap, tc, _, _, config) + } + + private predicate validAp(Ap ap, Configuration config) { + revFlow(_, _, _, _, ap, config) and ap instanceof ApNil + or + exists(TypedContent head, Ap tail | + consCand(head, tail, config) and + ap = apCons(head, tail) + ) + } + + predicate consCand(TypedContent tc, Ap ap, Configuration config) { + revConsCand(tc, ap, config) and + validAp(ap, config) + } + + pragma[noinline] + private predicate parameterFlow( + ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ) { + revFlow(p, _, true, apSome(ap0), ap, config) and + c = p.getEnclosingCallable() + } + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | + parameterFlow(p, ap, ap0, c, config) and + c = ret.getEnclosingCallable() and + revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), + pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and + fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and + kind = ret.getKind() and + p.getPosition() = pos and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists( + Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + | + revFlow(arg, state, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + + predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, argAp, ap, config) + ) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | consCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and + states = count(FlowState state | revFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | + revFlow(n, state, b, retAp, ap, config) + ) + } + /* End: Stage logic. */ + } +} + +private module BooleanCallContext { + class Cc extends boolean { + Cc() { this in [true, false] } + } + + class CcCall extends Cc { + CcCall() { this = true } + } + + /** Holds if the call context may be `call`. */ + predicate matchesCall(CcCall cc, DataFlowCall call) { any() } + + class CcNoCall extends Cc { + CcNoCall() { this = false } + } + + Cc ccNone() { result = false } + + CcCall ccSomeCall() { result = true } + + class LocalCc = Unit; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } +} + +private module Level1CallContext { class Cc = CallContext; class CcCall = CallContextCall; + pragma[inline] + predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } + class CcNoCall = CallContextNoCall; Cc ccNone() { result instanceof CallContextAny } CcCall ccSomeCall() { result instanceof CallContextSomeCall } - private class LocalCc = Unit; + module NoLocalCallContext { + class LocalCc = Unit; - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSiteDispatch(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSiteDispatch(call, c) + then result = TSpecificCall(call) + else result = TSomeCall() + } + } + + module LocalCallContext { + class LocalCc = LocalCallContext; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { + result = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + node.getEnclosingCallable()) + } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() + } } bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { checkCallContextReturn(innercc, c, call) and if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() } +} - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } +private module Stage2Param implements MkStage::StageParam { + private module PrevStage = Stage1; + + class Ap extends boolean { + Ap() { this in [true, false] } + } + + class ApNil extends Ap { + ApNil() { this = false } + } + + bindingset[result, ap] + PrevStage::Ap getApprox(Ap ap) { any() } + + ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + + pragma[inline] + Content getHeadContent(Ap ap) { exists(result) and ap = true } + + class ApOption = BooleanOption; + + ApOption apNone() { result = TBooleanNone() } + + ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + + import Level1CallContext + import NoLocalCallContext bindingset[node1, state1, config] bindingset[node2, state2, config] - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -1221,9 +1906,9 @@ private module Stage2 { exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/5; - private predicate flowIntoCall = flowIntoCallNodeCand1/5; + predicate flowIntoCall = flowIntoCallNodeCand1/5; pragma[nomagic] private predicate expectsContentCand(NodeEx node, Configuration config) { @@ -1235,7 +1920,7 @@ private module Stage2 { } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and not stateBarrier(node, state, config) and @@ -1248,544 +1933,11 @@ private module Stage2 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 2 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 2 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage2 = MkStage::Stage; + pragma[nomagic] private predicate flowOutOfCallNodeCand2( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config @@ -1883,14 +2035,13 @@ private module LocalFlowBigStep { ) { additionalLocalFlowStepNodeCand1(node1, node2, config) and state1 = state2 and - Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false, - pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false, + Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false, pragma[only_bind_into](config)) or additionalLocalStateStep(node1, state1, node2, state2, config) and - Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config)) + Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config)) } /** @@ -1967,26 +2118,24 @@ private module LocalFlowBigStep { private import LocalFlowBigStep -private module Stage3 { - module PrevStage = Stage2; - - class ApApprox = PrevStage::Ap; +private module Stage3Param implements MkStage::StageParam { + private module PrevStage = Stage2; class Ap = AccessPathFront; class ApNil = AccessPathFrontNil; - private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } + PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } + Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathFrontOption; @@ -1994,44 +2143,18 @@ private module Stage3 { ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - class Cc = boolean; + import BooleanCallContext - class CcCall extends Cc { - CcCall() { this = true } - - /** Holds if this call context may be `call`. */ - predicate matchesCall(DataFlowCall call) { any() } - } - - class CcNoCall extends Cc { - CcNoCall() { this = false } - } - - Cc ccNone() { result = false } - - CcCall ccSomeCall() { result = true } - - private class LocalCc = Unit; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/5; - private predicate flowIntoCall = flowIntoCallNodeCand2/5; + predicate flowIntoCall = flowIntoCallNodeCand2/5; pragma[nomagic] private predicate clearSet(NodeEx node, ContentSet c, Configuration config) { @@ -2067,7 +2190,7 @@ private module Stage3 { private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { exists(state) and exists(config) and not clear(node, ap, config) and @@ -2080,548 +2203,15 @@ private module Stage3 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { + predicate typecheckStore(Ap ap, DataFlowType contentType) { // We need to typecheck stores here, since reverse flow through a getter // might have a different type here compared to inside the getter. compatibleTypes(ap.getType(), contentType) } - - /* Begin: Stage 3 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 3 logic. */ } +private module Stage3 = MkStage::Stage; + /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` * and remains relevant for the following pruning stage. @@ -2644,7 +2234,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and nodes = strictcount(NodeEx n, FlowState state | - Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) or flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) ) and @@ -2828,26 +2418,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } } -private module Stage4 { - module PrevStage = Stage3; - - class ApApprox = PrevStage::Ap; +private module Stage4Param implements MkStage::StageParam { + private module PrevStage = Stage3; class Ap = AccessPathApprox; class ApNil = AccessPathApproxNil; - private ApApprox getApprox(Ap ap) { result = ap.getFront() } + PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } + Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathApproxOption; @@ -2855,38 +2443,10 @@ private module Stage4 { ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - class Cc = CallContext; + import Level1CallContext + import LocalCallContext - class CcCall = CallContextCall; - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - private class LocalCc = LocalCallContext; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() - } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { - result = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - node.getEnclosingCallable()) - } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -2894,575 +2454,40 @@ private module Stage4 { } pragma[nomagic] - private predicate flowOutOfCall( + predicate flowOutOfCall( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } pragma[nomagic] - private predicate flowIntoCall( + predicate flowIntoCall( DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } // Type checking is not necessary here as it has already been done in stage 3. bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 4 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 4 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage4 = MkStage::Stage; + bindingset[conf, result] private Configuration unbindConf(Configuration conf) { exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) @@ -3495,7 +2520,7 @@ private newtype TSummaryCtx = TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and - Stage4::revFlow(p, state, _, _, _, config) + Stage4::revFlow(p, state, _, config) ) } @@ -3553,7 +2578,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { result = strictcount(NodeEx n, FlowState state | - Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config) + Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config) ) } @@ -3667,7 +2692,7 @@ private newtype TPathNode = exists(PathNodeMid mid | pathStep(mid, node, state, cc, sc, ap) and pragma[only_bind_into](config) = mid.getConfiguration() and - Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config)) + Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config)) ) } or TPathNodeSink(NodeEx node, FlowState state, Configuration config) { @@ -4207,7 +3232,7 @@ private NodeEx getAnOutNodeFlow( ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result.asNode() = kind.getAnOutNode(call) and - Stage4::revFlow(result, _, _, _, apa, config) + Stage4::revFlow(result, _, apa, config) } /** @@ -4243,7 +3268,7 @@ private predicate parameterCand( DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config ) { exists(ParamNodeEx p | - Stage4::revFlow(p, _, _, _, apa, config) and + Stage4::revFlow(p, _, apa, config) and p.isParameterOf(callable, pos) ) } diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll index a076f7a2e45..22c3bd2466e 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,12 +944,9 @@ private module Stage1 { predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) } bindingset[node, state, config] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, toReturn, pragma[only_bind_into](config)) and + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, _, pragma[only_bind_into](config)) and exists(state) and - exists(returnAp) and exists(ap) } @@ -1142,66 +1139,754 @@ private predicate flowIntoCallNodeCand1( ) } -private module Stage2 { - module PrevStage = Stage1; +private signature module StageSig { + class Ap; + predicate revFlow(NodeEx node, Configuration config); + + bindingset[node, state, config] + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config); + + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ); + + predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config); +} + +private module MkStage { class ApApprox = PrevStage::Ap; - class Ap = boolean; + signature module StageParam { + class Ap; - class ApNil extends Ap { - ApNil() { this = false } + class ApNil extends Ap; + + bindingset[result, ap] + ApApprox getApprox(Ap ap); + + ApNil getApNil(NodeEx node); + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail); + + Content getHeadContent(Ap ap); + + class ApOption; + + ApOption apNone(); + + ApOption apSome(Ap ap); + + class Cc; + + class CcCall extends Cc; + + // TODO: member predicate on CcCall + predicate matchesCall(CcCall cc, DataFlowCall call); + + class CcNoCall extends Cc; + + Cc ccNone(); + + CcCall ccSomeCall(); + + class LocalCc; + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc); + + bindingset[node1, state1, config] + bindingset[node2, state2, config] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ); + + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + ); + + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ); + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config); + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType); } - bindingset[result, ap] - private ApApprox getApprox(Ap ap) { any() } + module Stage implements StageSig { + import Param - private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) } + /* Begin: Stage logic. */ + bindingset[result, apa] + private ApApprox unbindApa(ApApprox apa) { + pragma[only_bind_out](apa) = pragma[only_bind_out](result) + } - bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, + Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, + pragma[only_bind_into](config)) and + matchesCall(ccc, call) + } - pragma[inline] - private Content getHeadContent(Ap ap) { exists(result) and ap = true } + /** + * Holds if `node` is reachable with access path `ap` from a source in the + * configuration `config`. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. + */ + pragma[nomagic] + predicate fwdFlow( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + fwdFlow0(node, state, cc, argAp, ap, config) and + PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and + filter(node, state, ap, config) + } - class ApOption = BooleanOption; + pragma[nomagic] + private predicate fwdFlow0( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + sourceNode(node, state, config) and + (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and + argAp = apNone() and + ap = getApNil(node) + or + exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | + fwdFlow(mid, state0, cc, argAp, ap0, config) and + localCc = getLocalCc(mid, cc) + | + localStep(mid, state0, node, state, true, _, config, localCc) and + ap = ap0 + or + localStep(mid, state0, node, state, false, ap, config, localCc) and + ap0 instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + jumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStateStep(mid, state0, node, state, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + // store + exists(TypedContent tc, Ap ap0 | + fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + ap = apCons(tc, ap0) + ) + or + // read + exists(Ap ap0, Content c | + fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowConsCand(ap0, c, ap, config) + ) + or + // flow into a callable + exists(ApApprox apa | + fwdFlowIn(_, node, state, _, cc, _, ap, config) and + apa = getApprox(ap) and + if PrevStage::parameterMayFlowThrough(node, _, apa, config) + then argAp = apSome(ap) + else argAp = apNone() + ) + or + // flow out of a callable + fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) + ) + } - ApOption apNone() { result = TBooleanNone() } + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + exists(DataFlowType contentType | + fwdFlow(node1, state, cc, argAp, ap1, config) and + PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and + typecheckStore(ap1, contentType) + ) + } - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + /** + * Holds if forward flow with access path `tail` reaches a store of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(TypedContent tc | + fwdFlowStore(_, tail, tc, _, _, _, _, config) and + tc.getContent() = c and + cons = apCons(tc, tail) + ) + } + pragma[nomagic] + private predicate fwdFlowRead( + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + fwdFlow(node1, state, cc, argAp, ap, config) and + PrevStage::readStepCand(node1, c, node2, config) and + getHeadContent(ap) = c + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, + Ap ap, Configuration config + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, state, outercc, argAp, ap, config) and + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutNotFromArg( + NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + ) { + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, argAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + ccOut = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + ) + } + + pragma[nomagic] + private predicate storeStepFwd( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config + ) { + fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + ap2 = apCons(tc, ap1) and + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + } + + private predicate readStepFwd( + NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config + ) { + fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowConsCand(ap1, c, ap2, config) + } + + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and + fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), + pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + + pragma[nomagic] + private predicate returnNodeMayFlowThrough( + RetNodeEx ret, FlowState state, Ap ap, Configuration config + ) { + fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from the + * enclosing callable in order to reach a sink, and if so, `returnAp` records + * the access path of the returned value. + */ + pragma[nomagic] + predicate revFlow( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + revFlow0(node, state, toReturn, returnAp, ap, config) and + fwdFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + fwdFlow(node, state, _, _, ap, config) and + sinkNode(node, state, config) and + (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid, FlowState state0 | + localStep(node, state, mid, state0, true, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, ap, config) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, state, _, _, ap, config) and + toReturn = false and + returnAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStep(node, mid, config) and + revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStateStep(node, state, mid, state0, config) and + revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, + pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowConsCand(ap0, c, ap, config) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, state, toReturn, returnAp, ap0, config) and + readStepFwd(node, ap, _, mid, ap0, config) + ) + or + // flow into a callable + revFlowInNotToReturn(node, state, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + or + // flow out of a callable + revFlowOut(_, node, state, _, _, ap, config) and + toReturn = true and + if returnNodeMayFlowThrough(node, state, ap, config) + then returnAp = apSome(ap) + else returnAp = apNone() + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, + boolean toReturn, ApOption returnAp, Configuration config + ) { + revFlow(mid, state, toReturn, returnAp, ap0, config) and + storeStepFwd(node, ap, tc, mid, ap0, config) and + tc.getContent() = c + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, _, tail, config) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0, config) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, + Configuration config + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, state, toReturn, returnAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInNotToReturn( + ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, FlowState state, CcCall ccc | + revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and + fwdFlow(ret, state, ccc, apSome(_), ap, config) and + matchesCall(ccc, call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Ap ap2, Content c | + PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and + revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and + revFlowConsCand(ap2, c, ap1, config) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and + readStepFwd(node1, ap1, c, node2, ap2, config) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, + pragma[only_bind_into](config)) + ) + } + + predicate revFlow(NodeEx node, FlowState state, Configuration config) { + revFlow(node, state, _, _, _, config) + } + + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, ap, config) + } + + private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepFwd(_, ap, tc, _, _, config) + } + + private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepCand(_, ap, tc, _, _, config) + } + + private predicate validAp(Ap ap, Configuration config) { + revFlow(_, _, _, _, ap, config) and ap instanceof ApNil + or + exists(TypedContent head, Ap tail | + consCand(head, tail, config) and + ap = apCons(head, tail) + ) + } + + predicate consCand(TypedContent tc, Ap ap, Configuration config) { + revConsCand(tc, ap, config) and + validAp(ap, config) + } + + pragma[noinline] + private predicate parameterFlow( + ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ) { + revFlow(p, _, true, apSome(ap0), ap, config) and + c = p.getEnclosingCallable() + } + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | + parameterFlow(p, ap, ap0, c, config) and + c = ret.getEnclosingCallable() and + revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), + pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and + fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and + kind = ret.getKind() and + p.getPosition() = pos and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists( + Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + | + revFlow(arg, state, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + + predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, argAp, ap, config) + ) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | consCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and + states = count(FlowState state | revFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | + revFlow(n, state, b, retAp, ap, config) + ) + } + /* End: Stage logic. */ + } +} + +private module BooleanCallContext { + class Cc extends boolean { + Cc() { this in [true, false] } + } + + class CcCall extends Cc { + CcCall() { this = true } + } + + /** Holds if the call context may be `call`. */ + predicate matchesCall(CcCall cc, DataFlowCall call) { any() } + + class CcNoCall extends Cc { + CcNoCall() { this = false } + } + + Cc ccNone() { result = false } + + CcCall ccSomeCall() { result = true } + + class LocalCc = Unit; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } +} + +private module Level1CallContext { class Cc = CallContext; class CcCall = CallContextCall; + pragma[inline] + predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } + class CcNoCall = CallContextNoCall; Cc ccNone() { result instanceof CallContextAny } CcCall ccSomeCall() { result instanceof CallContextSomeCall } - private class LocalCc = Unit; + module NoLocalCallContext { + class LocalCc = Unit; - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSiteDispatch(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSiteDispatch(call, c) + then result = TSpecificCall(call) + else result = TSomeCall() + } + } + + module LocalCallContext { + class LocalCc = LocalCallContext; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { + result = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + node.getEnclosingCallable()) + } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() + } } bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { checkCallContextReturn(innercc, c, call) and if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() } +} - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } +private module Stage2Param implements MkStage::StageParam { + private module PrevStage = Stage1; + + class Ap extends boolean { + Ap() { this in [true, false] } + } + + class ApNil extends Ap { + ApNil() { this = false } + } + + bindingset[result, ap] + PrevStage::Ap getApprox(Ap ap) { any() } + + ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + + pragma[inline] + Content getHeadContent(Ap ap) { exists(result) and ap = true } + + class ApOption = BooleanOption; + + ApOption apNone() { result = TBooleanNone() } + + ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + + import Level1CallContext + import NoLocalCallContext bindingset[node1, state1, config] bindingset[node2, state2, config] - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -1221,9 +1906,9 @@ private module Stage2 { exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/5; - private predicate flowIntoCall = flowIntoCallNodeCand1/5; + predicate flowIntoCall = flowIntoCallNodeCand1/5; pragma[nomagic] private predicate expectsContentCand(NodeEx node, Configuration config) { @@ -1235,7 +1920,7 @@ private module Stage2 { } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and not stateBarrier(node, state, config) and @@ -1248,544 +1933,11 @@ private module Stage2 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 2 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 2 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage2 = MkStage::Stage; + pragma[nomagic] private predicate flowOutOfCallNodeCand2( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config @@ -1883,14 +2035,13 @@ private module LocalFlowBigStep { ) { additionalLocalFlowStepNodeCand1(node1, node2, config) and state1 = state2 and - Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false, - pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false, + Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false, pragma[only_bind_into](config)) or additionalLocalStateStep(node1, state1, node2, state2, config) and - Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config)) + Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config)) } /** @@ -1967,26 +2118,24 @@ private module LocalFlowBigStep { private import LocalFlowBigStep -private module Stage3 { - module PrevStage = Stage2; - - class ApApprox = PrevStage::Ap; +private module Stage3Param implements MkStage::StageParam { + private module PrevStage = Stage2; class Ap = AccessPathFront; class ApNil = AccessPathFrontNil; - private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } + PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } + Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathFrontOption; @@ -1994,44 +2143,18 @@ private module Stage3 { ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - class Cc = boolean; + import BooleanCallContext - class CcCall extends Cc { - CcCall() { this = true } - - /** Holds if this call context may be `call`. */ - predicate matchesCall(DataFlowCall call) { any() } - } - - class CcNoCall extends Cc { - CcNoCall() { this = false } - } - - Cc ccNone() { result = false } - - CcCall ccSomeCall() { result = true } - - private class LocalCc = Unit; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/5; - private predicate flowIntoCall = flowIntoCallNodeCand2/5; + predicate flowIntoCall = flowIntoCallNodeCand2/5; pragma[nomagic] private predicate clearSet(NodeEx node, ContentSet c, Configuration config) { @@ -2067,7 +2190,7 @@ private module Stage3 { private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { exists(state) and exists(config) and not clear(node, ap, config) and @@ -2080,548 +2203,15 @@ private module Stage3 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { + predicate typecheckStore(Ap ap, DataFlowType contentType) { // We need to typecheck stores here, since reverse flow through a getter // might have a different type here compared to inside the getter. compatibleTypes(ap.getType(), contentType) } - - /* Begin: Stage 3 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 3 logic. */ } +private module Stage3 = MkStage::Stage; + /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` * and remains relevant for the following pruning stage. @@ -2644,7 +2234,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and nodes = strictcount(NodeEx n, FlowState state | - Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) or flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) ) and @@ -2828,26 +2418,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } } -private module Stage4 { - module PrevStage = Stage3; - - class ApApprox = PrevStage::Ap; +private module Stage4Param implements MkStage::StageParam { + private module PrevStage = Stage3; class Ap = AccessPathApprox; class ApNil = AccessPathApproxNil; - private ApApprox getApprox(Ap ap) { result = ap.getFront() } + PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } + Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathApproxOption; @@ -2855,38 +2443,10 @@ private module Stage4 { ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - class Cc = CallContext; + import Level1CallContext + import LocalCallContext - class CcCall = CallContextCall; - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - private class LocalCc = LocalCallContext; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() - } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { - result = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - node.getEnclosingCallable()) - } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -2894,575 +2454,40 @@ private module Stage4 { } pragma[nomagic] - private predicate flowOutOfCall( + predicate flowOutOfCall( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } pragma[nomagic] - private predicate flowIntoCall( + predicate flowIntoCall( DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } // Type checking is not necessary here as it has already been done in stage 3. bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 4 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 4 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage4 = MkStage::Stage; + bindingset[conf, result] private Configuration unbindConf(Configuration conf) { exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) @@ -3495,7 +2520,7 @@ private newtype TSummaryCtx = TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and - Stage4::revFlow(p, state, _, _, _, config) + Stage4::revFlow(p, state, _, config) ) } @@ -3553,7 +2578,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { result = strictcount(NodeEx n, FlowState state | - Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config) + Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config) ) } @@ -3667,7 +2692,7 @@ private newtype TPathNode = exists(PathNodeMid mid | pathStep(mid, node, state, cc, sc, ap) and pragma[only_bind_into](config) = mid.getConfiguration() and - Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config)) + Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config)) ) } or TPathNodeSink(NodeEx node, FlowState state, Configuration config) { @@ -4207,7 +3232,7 @@ private NodeEx getAnOutNodeFlow( ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result.asNode() = kind.getAnOutNode(call) and - Stage4::revFlow(result, _, _, _, apa, config) + Stage4::revFlow(result, _, apa, config) } /** @@ -4243,7 +3268,7 @@ private predicate parameterCand( DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config ) { exists(ParamNodeEx p | - Stage4::revFlow(p, _, _, _, apa, config) and + Stage4::revFlow(p, _, apa, config) and p.isParameterOf(callable, pos) ) } diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll index a076f7a2e45..22c3bd2466e 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,12 +944,9 @@ private module Stage1 { predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) } bindingset[node, state, config] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, toReturn, pragma[only_bind_into](config)) and + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, _, pragma[only_bind_into](config)) and exists(state) and - exists(returnAp) and exists(ap) } @@ -1142,66 +1139,754 @@ private predicate flowIntoCallNodeCand1( ) } -private module Stage2 { - module PrevStage = Stage1; +private signature module StageSig { + class Ap; + predicate revFlow(NodeEx node, Configuration config); + + bindingset[node, state, config] + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config); + + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ); + + predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config); +} + +private module MkStage { class ApApprox = PrevStage::Ap; - class Ap = boolean; + signature module StageParam { + class Ap; - class ApNil extends Ap { - ApNil() { this = false } + class ApNil extends Ap; + + bindingset[result, ap] + ApApprox getApprox(Ap ap); + + ApNil getApNil(NodeEx node); + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail); + + Content getHeadContent(Ap ap); + + class ApOption; + + ApOption apNone(); + + ApOption apSome(Ap ap); + + class Cc; + + class CcCall extends Cc; + + // TODO: member predicate on CcCall + predicate matchesCall(CcCall cc, DataFlowCall call); + + class CcNoCall extends Cc; + + Cc ccNone(); + + CcCall ccSomeCall(); + + class LocalCc; + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc); + + bindingset[node1, state1, config] + bindingset[node2, state2, config] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ); + + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + ); + + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ); + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config); + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType); } - bindingset[result, ap] - private ApApprox getApprox(Ap ap) { any() } + module Stage implements StageSig { + import Param - private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) } + /* Begin: Stage logic. */ + bindingset[result, apa] + private ApApprox unbindApa(ApApprox apa) { + pragma[only_bind_out](apa) = pragma[only_bind_out](result) + } - bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, + Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, + pragma[only_bind_into](config)) and + matchesCall(ccc, call) + } - pragma[inline] - private Content getHeadContent(Ap ap) { exists(result) and ap = true } + /** + * Holds if `node` is reachable with access path `ap` from a source in the + * configuration `config`. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. + */ + pragma[nomagic] + predicate fwdFlow( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + fwdFlow0(node, state, cc, argAp, ap, config) and + PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and + filter(node, state, ap, config) + } - class ApOption = BooleanOption; + pragma[nomagic] + private predicate fwdFlow0( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + sourceNode(node, state, config) and + (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and + argAp = apNone() and + ap = getApNil(node) + or + exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | + fwdFlow(mid, state0, cc, argAp, ap0, config) and + localCc = getLocalCc(mid, cc) + | + localStep(mid, state0, node, state, true, _, config, localCc) and + ap = ap0 + or + localStep(mid, state0, node, state, false, ap, config, localCc) and + ap0 instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + jumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStateStep(mid, state0, node, state, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + // store + exists(TypedContent tc, Ap ap0 | + fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + ap = apCons(tc, ap0) + ) + or + // read + exists(Ap ap0, Content c | + fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowConsCand(ap0, c, ap, config) + ) + or + // flow into a callable + exists(ApApprox apa | + fwdFlowIn(_, node, state, _, cc, _, ap, config) and + apa = getApprox(ap) and + if PrevStage::parameterMayFlowThrough(node, _, apa, config) + then argAp = apSome(ap) + else argAp = apNone() + ) + or + // flow out of a callable + fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) + ) + } - ApOption apNone() { result = TBooleanNone() } + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + exists(DataFlowType contentType | + fwdFlow(node1, state, cc, argAp, ap1, config) and + PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and + typecheckStore(ap1, contentType) + ) + } - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + /** + * Holds if forward flow with access path `tail` reaches a store of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(TypedContent tc | + fwdFlowStore(_, tail, tc, _, _, _, _, config) and + tc.getContent() = c and + cons = apCons(tc, tail) + ) + } + pragma[nomagic] + private predicate fwdFlowRead( + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + fwdFlow(node1, state, cc, argAp, ap, config) and + PrevStage::readStepCand(node1, c, node2, config) and + getHeadContent(ap) = c + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, + Ap ap, Configuration config + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, state, outercc, argAp, ap, config) and + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutNotFromArg( + NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + ) { + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, argAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + ccOut = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + ) + } + + pragma[nomagic] + private predicate storeStepFwd( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config + ) { + fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + ap2 = apCons(tc, ap1) and + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + } + + private predicate readStepFwd( + NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config + ) { + fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowConsCand(ap1, c, ap2, config) + } + + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and + fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), + pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + + pragma[nomagic] + private predicate returnNodeMayFlowThrough( + RetNodeEx ret, FlowState state, Ap ap, Configuration config + ) { + fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from the + * enclosing callable in order to reach a sink, and if so, `returnAp` records + * the access path of the returned value. + */ + pragma[nomagic] + predicate revFlow( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + revFlow0(node, state, toReturn, returnAp, ap, config) and + fwdFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + fwdFlow(node, state, _, _, ap, config) and + sinkNode(node, state, config) and + (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid, FlowState state0 | + localStep(node, state, mid, state0, true, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, ap, config) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, state, _, _, ap, config) and + toReturn = false and + returnAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStep(node, mid, config) and + revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStateStep(node, state, mid, state0, config) and + revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, + pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowConsCand(ap0, c, ap, config) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, state, toReturn, returnAp, ap0, config) and + readStepFwd(node, ap, _, mid, ap0, config) + ) + or + // flow into a callable + revFlowInNotToReturn(node, state, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + or + // flow out of a callable + revFlowOut(_, node, state, _, _, ap, config) and + toReturn = true and + if returnNodeMayFlowThrough(node, state, ap, config) + then returnAp = apSome(ap) + else returnAp = apNone() + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, + boolean toReturn, ApOption returnAp, Configuration config + ) { + revFlow(mid, state, toReturn, returnAp, ap0, config) and + storeStepFwd(node, ap, tc, mid, ap0, config) and + tc.getContent() = c + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, _, tail, config) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0, config) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, + Configuration config + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, state, toReturn, returnAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInNotToReturn( + ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, FlowState state, CcCall ccc | + revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and + fwdFlow(ret, state, ccc, apSome(_), ap, config) and + matchesCall(ccc, call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Ap ap2, Content c | + PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and + revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and + revFlowConsCand(ap2, c, ap1, config) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and + readStepFwd(node1, ap1, c, node2, ap2, config) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, + pragma[only_bind_into](config)) + ) + } + + predicate revFlow(NodeEx node, FlowState state, Configuration config) { + revFlow(node, state, _, _, _, config) + } + + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, ap, config) + } + + private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepFwd(_, ap, tc, _, _, config) + } + + private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepCand(_, ap, tc, _, _, config) + } + + private predicate validAp(Ap ap, Configuration config) { + revFlow(_, _, _, _, ap, config) and ap instanceof ApNil + or + exists(TypedContent head, Ap tail | + consCand(head, tail, config) and + ap = apCons(head, tail) + ) + } + + predicate consCand(TypedContent tc, Ap ap, Configuration config) { + revConsCand(tc, ap, config) and + validAp(ap, config) + } + + pragma[noinline] + private predicate parameterFlow( + ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ) { + revFlow(p, _, true, apSome(ap0), ap, config) and + c = p.getEnclosingCallable() + } + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | + parameterFlow(p, ap, ap0, c, config) and + c = ret.getEnclosingCallable() and + revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), + pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and + fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and + kind = ret.getKind() and + p.getPosition() = pos and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists( + Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + | + revFlow(arg, state, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + + predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, argAp, ap, config) + ) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | consCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and + states = count(FlowState state | revFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | + revFlow(n, state, b, retAp, ap, config) + ) + } + /* End: Stage logic. */ + } +} + +private module BooleanCallContext { + class Cc extends boolean { + Cc() { this in [true, false] } + } + + class CcCall extends Cc { + CcCall() { this = true } + } + + /** Holds if the call context may be `call`. */ + predicate matchesCall(CcCall cc, DataFlowCall call) { any() } + + class CcNoCall extends Cc { + CcNoCall() { this = false } + } + + Cc ccNone() { result = false } + + CcCall ccSomeCall() { result = true } + + class LocalCc = Unit; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } +} + +private module Level1CallContext { class Cc = CallContext; class CcCall = CallContextCall; + pragma[inline] + predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } + class CcNoCall = CallContextNoCall; Cc ccNone() { result instanceof CallContextAny } CcCall ccSomeCall() { result instanceof CallContextSomeCall } - private class LocalCc = Unit; + module NoLocalCallContext { + class LocalCc = Unit; - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSiteDispatch(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSiteDispatch(call, c) + then result = TSpecificCall(call) + else result = TSomeCall() + } + } + + module LocalCallContext { + class LocalCc = LocalCallContext; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { + result = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + node.getEnclosingCallable()) + } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() + } } bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { checkCallContextReturn(innercc, c, call) and if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() } +} - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } +private module Stage2Param implements MkStage::StageParam { + private module PrevStage = Stage1; + + class Ap extends boolean { + Ap() { this in [true, false] } + } + + class ApNil extends Ap { + ApNil() { this = false } + } + + bindingset[result, ap] + PrevStage::Ap getApprox(Ap ap) { any() } + + ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + + pragma[inline] + Content getHeadContent(Ap ap) { exists(result) and ap = true } + + class ApOption = BooleanOption; + + ApOption apNone() { result = TBooleanNone() } + + ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + + import Level1CallContext + import NoLocalCallContext bindingset[node1, state1, config] bindingset[node2, state2, config] - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -1221,9 +1906,9 @@ private module Stage2 { exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/5; - private predicate flowIntoCall = flowIntoCallNodeCand1/5; + predicate flowIntoCall = flowIntoCallNodeCand1/5; pragma[nomagic] private predicate expectsContentCand(NodeEx node, Configuration config) { @@ -1235,7 +1920,7 @@ private module Stage2 { } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and not stateBarrier(node, state, config) and @@ -1248,544 +1933,11 @@ private module Stage2 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 2 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 2 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage2 = MkStage::Stage; + pragma[nomagic] private predicate flowOutOfCallNodeCand2( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config @@ -1883,14 +2035,13 @@ private module LocalFlowBigStep { ) { additionalLocalFlowStepNodeCand1(node1, node2, config) and state1 = state2 and - Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false, - pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false, + Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false, pragma[only_bind_into](config)) or additionalLocalStateStep(node1, state1, node2, state2, config) and - Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config)) + Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config)) } /** @@ -1967,26 +2118,24 @@ private module LocalFlowBigStep { private import LocalFlowBigStep -private module Stage3 { - module PrevStage = Stage2; - - class ApApprox = PrevStage::Ap; +private module Stage3Param implements MkStage::StageParam { + private module PrevStage = Stage2; class Ap = AccessPathFront; class ApNil = AccessPathFrontNil; - private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } + PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } + Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathFrontOption; @@ -1994,44 +2143,18 @@ private module Stage3 { ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - class Cc = boolean; + import BooleanCallContext - class CcCall extends Cc { - CcCall() { this = true } - - /** Holds if this call context may be `call`. */ - predicate matchesCall(DataFlowCall call) { any() } - } - - class CcNoCall extends Cc { - CcNoCall() { this = false } - } - - Cc ccNone() { result = false } - - CcCall ccSomeCall() { result = true } - - private class LocalCc = Unit; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/5; - private predicate flowIntoCall = flowIntoCallNodeCand2/5; + predicate flowIntoCall = flowIntoCallNodeCand2/5; pragma[nomagic] private predicate clearSet(NodeEx node, ContentSet c, Configuration config) { @@ -2067,7 +2190,7 @@ private module Stage3 { private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { exists(state) and exists(config) and not clear(node, ap, config) and @@ -2080,548 +2203,15 @@ private module Stage3 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { + predicate typecheckStore(Ap ap, DataFlowType contentType) { // We need to typecheck stores here, since reverse flow through a getter // might have a different type here compared to inside the getter. compatibleTypes(ap.getType(), contentType) } - - /* Begin: Stage 3 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 3 logic. */ } +private module Stage3 = MkStage::Stage; + /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` * and remains relevant for the following pruning stage. @@ -2644,7 +2234,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and nodes = strictcount(NodeEx n, FlowState state | - Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) or flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) ) and @@ -2828,26 +2418,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } } -private module Stage4 { - module PrevStage = Stage3; - - class ApApprox = PrevStage::Ap; +private module Stage4Param implements MkStage::StageParam { + private module PrevStage = Stage3; class Ap = AccessPathApprox; class ApNil = AccessPathApproxNil; - private ApApprox getApprox(Ap ap) { result = ap.getFront() } + PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } + Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathApproxOption; @@ -2855,38 +2443,10 @@ private module Stage4 { ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - class Cc = CallContext; + import Level1CallContext + import LocalCallContext - class CcCall = CallContextCall; - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - private class LocalCc = LocalCallContext; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() - } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { - result = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - node.getEnclosingCallable()) - } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -2894,575 +2454,40 @@ private module Stage4 { } pragma[nomagic] - private predicate flowOutOfCall( + predicate flowOutOfCall( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } pragma[nomagic] - private predicate flowIntoCall( + predicate flowIntoCall( DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } // Type checking is not necessary here as it has already been done in stage 3. bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 4 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 4 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage4 = MkStage::Stage; + bindingset[conf, result] private Configuration unbindConf(Configuration conf) { exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) @@ -3495,7 +2520,7 @@ private newtype TSummaryCtx = TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and - Stage4::revFlow(p, state, _, _, _, config) + Stage4::revFlow(p, state, _, config) ) } @@ -3553,7 +2578,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { result = strictcount(NodeEx n, FlowState state | - Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config) + Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config) ) } @@ -3667,7 +2692,7 @@ private newtype TPathNode = exists(PathNodeMid mid | pathStep(mid, node, state, cc, sc, ap) and pragma[only_bind_into](config) = mid.getConfiguration() and - Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config)) + Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config)) ) } or TPathNodeSink(NodeEx node, FlowState state, Configuration config) { @@ -4207,7 +3232,7 @@ private NodeEx getAnOutNodeFlow( ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result.asNode() = kind.getAnOutNode(call) and - Stage4::revFlow(result, _, _, _, apa, config) + Stage4::revFlow(result, _, apa, config) } /** @@ -4243,7 +3268,7 @@ private predicate parameterCand( DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config ) { exists(ParamNodeEx p | - Stage4::revFlow(p, _, _, _, apa, config) and + Stage4::revFlow(p, _, apa, config) and p.isParameterOf(callable, pos) ) } diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll index a076f7a2e45..22c3bd2466e 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,12 +944,9 @@ private module Stage1 { predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) } bindingset[node, state, config] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, toReturn, pragma[only_bind_into](config)) and + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, _, pragma[only_bind_into](config)) and exists(state) and - exists(returnAp) and exists(ap) } @@ -1142,66 +1139,754 @@ private predicate flowIntoCallNodeCand1( ) } -private module Stage2 { - module PrevStage = Stage1; +private signature module StageSig { + class Ap; + predicate revFlow(NodeEx node, Configuration config); + + bindingset[node, state, config] + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config); + + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ); + + predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config); +} + +private module MkStage { class ApApprox = PrevStage::Ap; - class Ap = boolean; + signature module StageParam { + class Ap; - class ApNil extends Ap { - ApNil() { this = false } + class ApNil extends Ap; + + bindingset[result, ap] + ApApprox getApprox(Ap ap); + + ApNil getApNil(NodeEx node); + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail); + + Content getHeadContent(Ap ap); + + class ApOption; + + ApOption apNone(); + + ApOption apSome(Ap ap); + + class Cc; + + class CcCall extends Cc; + + // TODO: member predicate on CcCall + predicate matchesCall(CcCall cc, DataFlowCall call); + + class CcNoCall extends Cc; + + Cc ccNone(); + + CcCall ccSomeCall(); + + class LocalCc; + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc); + + bindingset[node1, state1, config] + bindingset[node2, state2, config] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ); + + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + ); + + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ); + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config); + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType); } - bindingset[result, ap] - private ApApprox getApprox(Ap ap) { any() } + module Stage implements StageSig { + import Param - private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) } + /* Begin: Stage logic. */ + bindingset[result, apa] + private ApApprox unbindApa(ApApprox apa) { + pragma[only_bind_out](apa) = pragma[only_bind_out](result) + } - bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, + Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, + pragma[only_bind_into](config)) and + matchesCall(ccc, call) + } - pragma[inline] - private Content getHeadContent(Ap ap) { exists(result) and ap = true } + /** + * Holds if `node` is reachable with access path `ap` from a source in the + * configuration `config`. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. + */ + pragma[nomagic] + predicate fwdFlow( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + fwdFlow0(node, state, cc, argAp, ap, config) and + PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and + filter(node, state, ap, config) + } - class ApOption = BooleanOption; + pragma[nomagic] + private predicate fwdFlow0( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + sourceNode(node, state, config) and + (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and + argAp = apNone() and + ap = getApNil(node) + or + exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | + fwdFlow(mid, state0, cc, argAp, ap0, config) and + localCc = getLocalCc(mid, cc) + | + localStep(mid, state0, node, state, true, _, config, localCc) and + ap = ap0 + or + localStep(mid, state0, node, state, false, ap, config, localCc) and + ap0 instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + jumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStateStep(mid, state0, node, state, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + // store + exists(TypedContent tc, Ap ap0 | + fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + ap = apCons(tc, ap0) + ) + or + // read + exists(Ap ap0, Content c | + fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowConsCand(ap0, c, ap, config) + ) + or + // flow into a callable + exists(ApApprox apa | + fwdFlowIn(_, node, state, _, cc, _, ap, config) and + apa = getApprox(ap) and + if PrevStage::parameterMayFlowThrough(node, _, apa, config) + then argAp = apSome(ap) + else argAp = apNone() + ) + or + // flow out of a callable + fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) + ) + } - ApOption apNone() { result = TBooleanNone() } + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + exists(DataFlowType contentType | + fwdFlow(node1, state, cc, argAp, ap1, config) and + PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and + typecheckStore(ap1, contentType) + ) + } - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + /** + * Holds if forward flow with access path `tail` reaches a store of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(TypedContent tc | + fwdFlowStore(_, tail, tc, _, _, _, _, config) and + tc.getContent() = c and + cons = apCons(tc, tail) + ) + } + pragma[nomagic] + private predicate fwdFlowRead( + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + fwdFlow(node1, state, cc, argAp, ap, config) and + PrevStage::readStepCand(node1, c, node2, config) and + getHeadContent(ap) = c + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, + Ap ap, Configuration config + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, state, outercc, argAp, ap, config) and + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutNotFromArg( + NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + ) { + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, argAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + ccOut = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + ) + } + + pragma[nomagic] + private predicate storeStepFwd( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config + ) { + fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + ap2 = apCons(tc, ap1) and + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + } + + private predicate readStepFwd( + NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config + ) { + fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowConsCand(ap1, c, ap2, config) + } + + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and + fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), + pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + + pragma[nomagic] + private predicate returnNodeMayFlowThrough( + RetNodeEx ret, FlowState state, Ap ap, Configuration config + ) { + fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from the + * enclosing callable in order to reach a sink, and if so, `returnAp` records + * the access path of the returned value. + */ + pragma[nomagic] + predicate revFlow( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + revFlow0(node, state, toReturn, returnAp, ap, config) and + fwdFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + fwdFlow(node, state, _, _, ap, config) and + sinkNode(node, state, config) and + (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid, FlowState state0 | + localStep(node, state, mid, state0, true, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, ap, config) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, state, _, _, ap, config) and + toReturn = false and + returnAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStep(node, mid, config) and + revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStateStep(node, state, mid, state0, config) and + revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, + pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowConsCand(ap0, c, ap, config) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, state, toReturn, returnAp, ap0, config) and + readStepFwd(node, ap, _, mid, ap0, config) + ) + or + // flow into a callable + revFlowInNotToReturn(node, state, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + or + // flow out of a callable + revFlowOut(_, node, state, _, _, ap, config) and + toReturn = true and + if returnNodeMayFlowThrough(node, state, ap, config) + then returnAp = apSome(ap) + else returnAp = apNone() + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, + boolean toReturn, ApOption returnAp, Configuration config + ) { + revFlow(mid, state, toReturn, returnAp, ap0, config) and + storeStepFwd(node, ap, tc, mid, ap0, config) and + tc.getContent() = c + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, _, tail, config) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0, config) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, + Configuration config + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, state, toReturn, returnAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInNotToReturn( + ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, FlowState state, CcCall ccc | + revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and + fwdFlow(ret, state, ccc, apSome(_), ap, config) and + matchesCall(ccc, call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Ap ap2, Content c | + PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and + revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and + revFlowConsCand(ap2, c, ap1, config) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and + readStepFwd(node1, ap1, c, node2, ap2, config) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, + pragma[only_bind_into](config)) + ) + } + + predicate revFlow(NodeEx node, FlowState state, Configuration config) { + revFlow(node, state, _, _, _, config) + } + + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, ap, config) + } + + private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepFwd(_, ap, tc, _, _, config) + } + + private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepCand(_, ap, tc, _, _, config) + } + + private predicate validAp(Ap ap, Configuration config) { + revFlow(_, _, _, _, ap, config) and ap instanceof ApNil + or + exists(TypedContent head, Ap tail | + consCand(head, tail, config) and + ap = apCons(head, tail) + ) + } + + predicate consCand(TypedContent tc, Ap ap, Configuration config) { + revConsCand(tc, ap, config) and + validAp(ap, config) + } + + pragma[noinline] + private predicate parameterFlow( + ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ) { + revFlow(p, _, true, apSome(ap0), ap, config) and + c = p.getEnclosingCallable() + } + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | + parameterFlow(p, ap, ap0, c, config) and + c = ret.getEnclosingCallable() and + revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), + pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and + fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and + kind = ret.getKind() and + p.getPosition() = pos and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists( + Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + | + revFlow(arg, state, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + + predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, argAp, ap, config) + ) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | consCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and + states = count(FlowState state | revFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | + revFlow(n, state, b, retAp, ap, config) + ) + } + /* End: Stage logic. */ + } +} + +private module BooleanCallContext { + class Cc extends boolean { + Cc() { this in [true, false] } + } + + class CcCall extends Cc { + CcCall() { this = true } + } + + /** Holds if the call context may be `call`. */ + predicate matchesCall(CcCall cc, DataFlowCall call) { any() } + + class CcNoCall extends Cc { + CcNoCall() { this = false } + } + + Cc ccNone() { result = false } + + CcCall ccSomeCall() { result = true } + + class LocalCc = Unit; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } +} + +private module Level1CallContext { class Cc = CallContext; class CcCall = CallContextCall; + pragma[inline] + predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } + class CcNoCall = CallContextNoCall; Cc ccNone() { result instanceof CallContextAny } CcCall ccSomeCall() { result instanceof CallContextSomeCall } - private class LocalCc = Unit; + module NoLocalCallContext { + class LocalCc = Unit; - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSiteDispatch(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSiteDispatch(call, c) + then result = TSpecificCall(call) + else result = TSomeCall() + } + } + + module LocalCallContext { + class LocalCc = LocalCallContext; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { + result = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + node.getEnclosingCallable()) + } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() + } } bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { checkCallContextReturn(innercc, c, call) and if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() } +} - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } +private module Stage2Param implements MkStage::StageParam { + private module PrevStage = Stage1; + + class Ap extends boolean { + Ap() { this in [true, false] } + } + + class ApNil extends Ap { + ApNil() { this = false } + } + + bindingset[result, ap] + PrevStage::Ap getApprox(Ap ap) { any() } + + ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + + pragma[inline] + Content getHeadContent(Ap ap) { exists(result) and ap = true } + + class ApOption = BooleanOption; + + ApOption apNone() { result = TBooleanNone() } + + ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + + import Level1CallContext + import NoLocalCallContext bindingset[node1, state1, config] bindingset[node2, state2, config] - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -1221,9 +1906,9 @@ private module Stage2 { exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/5; - private predicate flowIntoCall = flowIntoCallNodeCand1/5; + predicate flowIntoCall = flowIntoCallNodeCand1/5; pragma[nomagic] private predicate expectsContentCand(NodeEx node, Configuration config) { @@ -1235,7 +1920,7 @@ private module Stage2 { } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and not stateBarrier(node, state, config) and @@ -1248,544 +1933,11 @@ private module Stage2 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 2 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 2 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage2 = MkStage::Stage; + pragma[nomagic] private predicate flowOutOfCallNodeCand2( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config @@ -1883,14 +2035,13 @@ private module LocalFlowBigStep { ) { additionalLocalFlowStepNodeCand1(node1, node2, config) and state1 = state2 and - Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false, - pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false, + Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false, pragma[only_bind_into](config)) or additionalLocalStateStep(node1, state1, node2, state2, config) and - Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config)) + Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config)) } /** @@ -1967,26 +2118,24 @@ private module LocalFlowBigStep { private import LocalFlowBigStep -private module Stage3 { - module PrevStage = Stage2; - - class ApApprox = PrevStage::Ap; +private module Stage3Param implements MkStage::StageParam { + private module PrevStage = Stage2; class Ap = AccessPathFront; class ApNil = AccessPathFrontNil; - private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } + PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } + Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathFrontOption; @@ -1994,44 +2143,18 @@ private module Stage3 { ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - class Cc = boolean; + import BooleanCallContext - class CcCall extends Cc { - CcCall() { this = true } - - /** Holds if this call context may be `call`. */ - predicate matchesCall(DataFlowCall call) { any() } - } - - class CcNoCall extends Cc { - CcNoCall() { this = false } - } - - Cc ccNone() { result = false } - - CcCall ccSomeCall() { result = true } - - private class LocalCc = Unit; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/5; - private predicate flowIntoCall = flowIntoCallNodeCand2/5; + predicate flowIntoCall = flowIntoCallNodeCand2/5; pragma[nomagic] private predicate clearSet(NodeEx node, ContentSet c, Configuration config) { @@ -2067,7 +2190,7 @@ private module Stage3 { private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { exists(state) and exists(config) and not clear(node, ap, config) and @@ -2080,548 +2203,15 @@ private module Stage3 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { + predicate typecheckStore(Ap ap, DataFlowType contentType) { // We need to typecheck stores here, since reverse flow through a getter // might have a different type here compared to inside the getter. compatibleTypes(ap.getType(), contentType) } - - /* Begin: Stage 3 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 3 logic. */ } +private module Stage3 = MkStage::Stage; + /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` * and remains relevant for the following pruning stage. @@ -2644,7 +2234,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and nodes = strictcount(NodeEx n, FlowState state | - Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) or flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) ) and @@ -2828,26 +2418,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } } -private module Stage4 { - module PrevStage = Stage3; - - class ApApprox = PrevStage::Ap; +private module Stage4Param implements MkStage::StageParam { + private module PrevStage = Stage3; class Ap = AccessPathApprox; class ApNil = AccessPathApproxNil; - private ApApprox getApprox(Ap ap) { result = ap.getFront() } + PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } + Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathApproxOption; @@ -2855,38 +2443,10 @@ private module Stage4 { ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - class Cc = CallContext; + import Level1CallContext + import LocalCallContext - class CcCall = CallContextCall; - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - private class LocalCc = LocalCallContext; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() - } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { - result = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - node.getEnclosingCallable()) - } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -2894,575 +2454,40 @@ private module Stage4 { } pragma[nomagic] - private predicate flowOutOfCall( + predicate flowOutOfCall( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } pragma[nomagic] - private predicate flowIntoCall( + predicate flowIntoCall( DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } // Type checking is not necessary here as it has already been done in stage 3. bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 4 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 4 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage4 = MkStage::Stage; + bindingset[conf, result] private Configuration unbindConf(Configuration conf) { exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) @@ -3495,7 +2520,7 @@ private newtype TSummaryCtx = TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and - Stage4::revFlow(p, state, _, _, _, config) + Stage4::revFlow(p, state, _, config) ) } @@ -3553,7 +2578,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { result = strictcount(NodeEx n, FlowState state | - Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config) + Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config) ) } @@ -3667,7 +2692,7 @@ private newtype TPathNode = exists(PathNodeMid mid | pathStep(mid, node, state, cc, sc, ap) and pragma[only_bind_into](config) = mid.getConfiguration() and - Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config)) + Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config)) ) } or TPathNodeSink(NodeEx node, FlowState state, Configuration config) { @@ -4207,7 +3232,7 @@ private NodeEx getAnOutNodeFlow( ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result.asNode() = kind.getAnOutNode(call) and - Stage4::revFlow(result, _, _, _, apa, config) + Stage4::revFlow(result, _, apa, config) } /** @@ -4243,7 +3268,7 @@ private predicate parameterCand( DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config ) { exists(ParamNodeEx p | - Stage4::revFlow(p, _, _, _, apa, config) and + Stage4::revFlow(p, _, apa, config) and p.isParameterOf(callable, pos) ) } diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll index a076f7a2e45..22c3bd2466e 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,12 +944,9 @@ private module Stage1 { predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) } bindingset[node, state, config] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, toReturn, pragma[only_bind_into](config)) and + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, _, pragma[only_bind_into](config)) and exists(state) and - exists(returnAp) and exists(ap) } @@ -1142,66 +1139,754 @@ private predicate flowIntoCallNodeCand1( ) } -private module Stage2 { - module PrevStage = Stage1; +private signature module StageSig { + class Ap; + predicate revFlow(NodeEx node, Configuration config); + + bindingset[node, state, config] + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config); + + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ); + + predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config); +} + +private module MkStage { class ApApprox = PrevStage::Ap; - class Ap = boolean; + signature module StageParam { + class Ap; - class ApNil extends Ap { - ApNil() { this = false } + class ApNil extends Ap; + + bindingset[result, ap] + ApApprox getApprox(Ap ap); + + ApNil getApNil(NodeEx node); + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail); + + Content getHeadContent(Ap ap); + + class ApOption; + + ApOption apNone(); + + ApOption apSome(Ap ap); + + class Cc; + + class CcCall extends Cc; + + // TODO: member predicate on CcCall + predicate matchesCall(CcCall cc, DataFlowCall call); + + class CcNoCall extends Cc; + + Cc ccNone(); + + CcCall ccSomeCall(); + + class LocalCc; + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc); + + bindingset[node1, state1, config] + bindingset[node2, state2, config] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ); + + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + ); + + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ); + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config); + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType); } - bindingset[result, ap] - private ApApprox getApprox(Ap ap) { any() } + module Stage implements StageSig { + import Param - private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) } + /* Begin: Stage logic. */ + bindingset[result, apa] + private ApApprox unbindApa(ApApprox apa) { + pragma[only_bind_out](apa) = pragma[only_bind_out](result) + } - bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, + Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, + pragma[only_bind_into](config)) and + matchesCall(ccc, call) + } - pragma[inline] - private Content getHeadContent(Ap ap) { exists(result) and ap = true } + /** + * Holds if `node` is reachable with access path `ap` from a source in the + * configuration `config`. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. + */ + pragma[nomagic] + predicate fwdFlow( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + fwdFlow0(node, state, cc, argAp, ap, config) and + PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and + filter(node, state, ap, config) + } - class ApOption = BooleanOption; + pragma[nomagic] + private predicate fwdFlow0( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + sourceNode(node, state, config) and + (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and + argAp = apNone() and + ap = getApNil(node) + or + exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | + fwdFlow(mid, state0, cc, argAp, ap0, config) and + localCc = getLocalCc(mid, cc) + | + localStep(mid, state0, node, state, true, _, config, localCc) and + ap = ap0 + or + localStep(mid, state0, node, state, false, ap, config, localCc) and + ap0 instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + jumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStateStep(mid, state0, node, state, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + // store + exists(TypedContent tc, Ap ap0 | + fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + ap = apCons(tc, ap0) + ) + or + // read + exists(Ap ap0, Content c | + fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowConsCand(ap0, c, ap, config) + ) + or + // flow into a callable + exists(ApApprox apa | + fwdFlowIn(_, node, state, _, cc, _, ap, config) and + apa = getApprox(ap) and + if PrevStage::parameterMayFlowThrough(node, _, apa, config) + then argAp = apSome(ap) + else argAp = apNone() + ) + or + // flow out of a callable + fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) + ) + } - ApOption apNone() { result = TBooleanNone() } + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + exists(DataFlowType contentType | + fwdFlow(node1, state, cc, argAp, ap1, config) and + PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and + typecheckStore(ap1, contentType) + ) + } - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + /** + * Holds if forward flow with access path `tail` reaches a store of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(TypedContent tc | + fwdFlowStore(_, tail, tc, _, _, _, _, config) and + tc.getContent() = c and + cons = apCons(tc, tail) + ) + } + pragma[nomagic] + private predicate fwdFlowRead( + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + fwdFlow(node1, state, cc, argAp, ap, config) and + PrevStage::readStepCand(node1, c, node2, config) and + getHeadContent(ap) = c + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, + Ap ap, Configuration config + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, state, outercc, argAp, ap, config) and + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutNotFromArg( + NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + ) { + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, argAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + ccOut = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + ) + } + + pragma[nomagic] + private predicate storeStepFwd( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config + ) { + fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + ap2 = apCons(tc, ap1) and + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + } + + private predicate readStepFwd( + NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config + ) { + fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowConsCand(ap1, c, ap2, config) + } + + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and + fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), + pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + + pragma[nomagic] + private predicate returnNodeMayFlowThrough( + RetNodeEx ret, FlowState state, Ap ap, Configuration config + ) { + fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from the + * enclosing callable in order to reach a sink, and if so, `returnAp` records + * the access path of the returned value. + */ + pragma[nomagic] + predicate revFlow( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + revFlow0(node, state, toReturn, returnAp, ap, config) and + fwdFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + fwdFlow(node, state, _, _, ap, config) and + sinkNode(node, state, config) and + (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid, FlowState state0 | + localStep(node, state, mid, state0, true, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, ap, config) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, state, _, _, ap, config) and + toReturn = false and + returnAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStep(node, mid, config) and + revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStateStep(node, state, mid, state0, config) and + revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, + pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowConsCand(ap0, c, ap, config) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, state, toReturn, returnAp, ap0, config) and + readStepFwd(node, ap, _, mid, ap0, config) + ) + or + // flow into a callable + revFlowInNotToReturn(node, state, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + or + // flow out of a callable + revFlowOut(_, node, state, _, _, ap, config) and + toReturn = true and + if returnNodeMayFlowThrough(node, state, ap, config) + then returnAp = apSome(ap) + else returnAp = apNone() + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, + boolean toReturn, ApOption returnAp, Configuration config + ) { + revFlow(mid, state, toReturn, returnAp, ap0, config) and + storeStepFwd(node, ap, tc, mid, ap0, config) and + tc.getContent() = c + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, _, tail, config) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0, config) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, + Configuration config + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, state, toReturn, returnAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInNotToReturn( + ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, FlowState state, CcCall ccc | + revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and + fwdFlow(ret, state, ccc, apSome(_), ap, config) and + matchesCall(ccc, call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Ap ap2, Content c | + PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and + revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and + revFlowConsCand(ap2, c, ap1, config) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and + readStepFwd(node1, ap1, c, node2, ap2, config) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, + pragma[only_bind_into](config)) + ) + } + + predicate revFlow(NodeEx node, FlowState state, Configuration config) { + revFlow(node, state, _, _, _, config) + } + + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, ap, config) + } + + private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepFwd(_, ap, tc, _, _, config) + } + + private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepCand(_, ap, tc, _, _, config) + } + + private predicate validAp(Ap ap, Configuration config) { + revFlow(_, _, _, _, ap, config) and ap instanceof ApNil + or + exists(TypedContent head, Ap tail | + consCand(head, tail, config) and + ap = apCons(head, tail) + ) + } + + predicate consCand(TypedContent tc, Ap ap, Configuration config) { + revConsCand(tc, ap, config) and + validAp(ap, config) + } + + pragma[noinline] + private predicate parameterFlow( + ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ) { + revFlow(p, _, true, apSome(ap0), ap, config) and + c = p.getEnclosingCallable() + } + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | + parameterFlow(p, ap, ap0, c, config) and + c = ret.getEnclosingCallable() and + revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), + pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and + fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and + kind = ret.getKind() and + p.getPosition() = pos and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists( + Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + | + revFlow(arg, state, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + + predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, argAp, ap, config) + ) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | consCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and + states = count(FlowState state | revFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | + revFlow(n, state, b, retAp, ap, config) + ) + } + /* End: Stage logic. */ + } +} + +private module BooleanCallContext { + class Cc extends boolean { + Cc() { this in [true, false] } + } + + class CcCall extends Cc { + CcCall() { this = true } + } + + /** Holds if the call context may be `call`. */ + predicate matchesCall(CcCall cc, DataFlowCall call) { any() } + + class CcNoCall extends Cc { + CcNoCall() { this = false } + } + + Cc ccNone() { result = false } + + CcCall ccSomeCall() { result = true } + + class LocalCc = Unit; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } +} + +private module Level1CallContext { class Cc = CallContext; class CcCall = CallContextCall; + pragma[inline] + predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } + class CcNoCall = CallContextNoCall; Cc ccNone() { result instanceof CallContextAny } CcCall ccSomeCall() { result instanceof CallContextSomeCall } - private class LocalCc = Unit; + module NoLocalCallContext { + class LocalCc = Unit; - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSiteDispatch(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSiteDispatch(call, c) + then result = TSpecificCall(call) + else result = TSomeCall() + } + } + + module LocalCallContext { + class LocalCc = LocalCallContext; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { + result = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + node.getEnclosingCallable()) + } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() + } } bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { checkCallContextReturn(innercc, c, call) and if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() } +} - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } +private module Stage2Param implements MkStage::StageParam { + private module PrevStage = Stage1; + + class Ap extends boolean { + Ap() { this in [true, false] } + } + + class ApNil extends Ap { + ApNil() { this = false } + } + + bindingset[result, ap] + PrevStage::Ap getApprox(Ap ap) { any() } + + ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + + pragma[inline] + Content getHeadContent(Ap ap) { exists(result) and ap = true } + + class ApOption = BooleanOption; + + ApOption apNone() { result = TBooleanNone() } + + ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + + import Level1CallContext + import NoLocalCallContext bindingset[node1, state1, config] bindingset[node2, state2, config] - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -1221,9 +1906,9 @@ private module Stage2 { exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/5; - private predicate flowIntoCall = flowIntoCallNodeCand1/5; + predicate flowIntoCall = flowIntoCallNodeCand1/5; pragma[nomagic] private predicate expectsContentCand(NodeEx node, Configuration config) { @@ -1235,7 +1920,7 @@ private module Stage2 { } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and not stateBarrier(node, state, config) and @@ -1248,544 +1933,11 @@ private module Stage2 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 2 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 2 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage2 = MkStage::Stage; + pragma[nomagic] private predicate flowOutOfCallNodeCand2( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config @@ -1883,14 +2035,13 @@ private module LocalFlowBigStep { ) { additionalLocalFlowStepNodeCand1(node1, node2, config) and state1 = state2 and - Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false, - pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false, + Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false, pragma[only_bind_into](config)) or additionalLocalStateStep(node1, state1, node2, state2, config) and - Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config)) + Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config)) } /** @@ -1967,26 +2118,24 @@ private module LocalFlowBigStep { private import LocalFlowBigStep -private module Stage3 { - module PrevStage = Stage2; - - class ApApprox = PrevStage::Ap; +private module Stage3Param implements MkStage::StageParam { + private module PrevStage = Stage2; class Ap = AccessPathFront; class ApNil = AccessPathFrontNil; - private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } + PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } + Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathFrontOption; @@ -1994,44 +2143,18 @@ private module Stage3 { ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - class Cc = boolean; + import BooleanCallContext - class CcCall extends Cc { - CcCall() { this = true } - - /** Holds if this call context may be `call`. */ - predicate matchesCall(DataFlowCall call) { any() } - } - - class CcNoCall extends Cc { - CcNoCall() { this = false } - } - - Cc ccNone() { result = false } - - CcCall ccSomeCall() { result = true } - - private class LocalCc = Unit; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/5; - private predicate flowIntoCall = flowIntoCallNodeCand2/5; + predicate flowIntoCall = flowIntoCallNodeCand2/5; pragma[nomagic] private predicate clearSet(NodeEx node, ContentSet c, Configuration config) { @@ -2067,7 +2190,7 @@ private module Stage3 { private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { exists(state) and exists(config) and not clear(node, ap, config) and @@ -2080,548 +2203,15 @@ private module Stage3 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { + predicate typecheckStore(Ap ap, DataFlowType contentType) { // We need to typecheck stores here, since reverse flow through a getter // might have a different type here compared to inside the getter. compatibleTypes(ap.getType(), contentType) } - - /* Begin: Stage 3 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 3 logic. */ } +private module Stage3 = MkStage::Stage; + /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` * and remains relevant for the following pruning stage. @@ -2644,7 +2234,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and nodes = strictcount(NodeEx n, FlowState state | - Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) or flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) ) and @@ -2828,26 +2418,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } } -private module Stage4 { - module PrevStage = Stage3; - - class ApApprox = PrevStage::Ap; +private module Stage4Param implements MkStage::StageParam { + private module PrevStage = Stage3; class Ap = AccessPathApprox; class ApNil = AccessPathApproxNil; - private ApApprox getApprox(Ap ap) { result = ap.getFront() } + PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } + Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathApproxOption; @@ -2855,38 +2443,10 @@ private module Stage4 { ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - class Cc = CallContext; + import Level1CallContext + import LocalCallContext - class CcCall = CallContextCall; - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - private class LocalCc = LocalCallContext; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() - } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { - result = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - node.getEnclosingCallable()) - } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -2894,575 +2454,40 @@ private module Stage4 { } pragma[nomagic] - private predicate flowOutOfCall( + predicate flowOutOfCall( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } pragma[nomagic] - private predicate flowIntoCall( + predicate flowIntoCall( DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } // Type checking is not necessary here as it has already been done in stage 3. bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 4 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 4 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage4 = MkStage::Stage; + bindingset[conf, result] private Configuration unbindConf(Configuration conf) { exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) @@ -3495,7 +2520,7 @@ private newtype TSummaryCtx = TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and - Stage4::revFlow(p, state, _, _, _, config) + Stage4::revFlow(p, state, _, config) ) } @@ -3553,7 +2578,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { result = strictcount(NodeEx n, FlowState state | - Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config) + Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config) ) } @@ -3667,7 +2692,7 @@ private newtype TPathNode = exists(PathNodeMid mid | pathStep(mid, node, state, cc, sc, ap) and pragma[only_bind_into](config) = mid.getConfiguration() and - Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config)) + Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config)) ) } or TPathNodeSink(NodeEx node, FlowState state, Configuration config) { @@ -4207,7 +3232,7 @@ private NodeEx getAnOutNodeFlow( ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result.asNode() = kind.getAnOutNode(call) and - Stage4::revFlow(result, _, _, _, apa, config) + Stage4::revFlow(result, _, apa, config) } /** @@ -4243,7 +3268,7 @@ private predicate parameterCand( DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config ) { exists(ParamNodeEx p | - Stage4::revFlow(p, _, _, _, apa, config) and + Stage4::revFlow(p, _, apa, config) and p.isParameterOf(callable, pos) ) } diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplForContentDataFlow.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplForContentDataFlow.qll index a076f7a2e45..22c3bd2466e 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplForContentDataFlow.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplForContentDataFlow.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,12 +944,9 @@ private module Stage1 { predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) } bindingset[node, state, config] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, toReturn, pragma[only_bind_into](config)) and + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, _, pragma[only_bind_into](config)) and exists(state) and - exists(returnAp) and exists(ap) } @@ -1142,66 +1139,754 @@ private predicate flowIntoCallNodeCand1( ) } -private module Stage2 { - module PrevStage = Stage1; +private signature module StageSig { + class Ap; + predicate revFlow(NodeEx node, Configuration config); + + bindingset[node, state, config] + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config); + + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ); + + predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config); +} + +private module MkStage { class ApApprox = PrevStage::Ap; - class Ap = boolean; + signature module StageParam { + class Ap; - class ApNil extends Ap { - ApNil() { this = false } + class ApNil extends Ap; + + bindingset[result, ap] + ApApprox getApprox(Ap ap); + + ApNil getApNil(NodeEx node); + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail); + + Content getHeadContent(Ap ap); + + class ApOption; + + ApOption apNone(); + + ApOption apSome(Ap ap); + + class Cc; + + class CcCall extends Cc; + + // TODO: member predicate on CcCall + predicate matchesCall(CcCall cc, DataFlowCall call); + + class CcNoCall extends Cc; + + Cc ccNone(); + + CcCall ccSomeCall(); + + class LocalCc; + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc); + + bindingset[node1, state1, config] + bindingset[node2, state2, config] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ); + + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + ); + + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ); + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config); + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType); } - bindingset[result, ap] - private ApApprox getApprox(Ap ap) { any() } + module Stage implements StageSig { + import Param - private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) } + /* Begin: Stage logic. */ + bindingset[result, apa] + private ApApprox unbindApa(ApApprox apa) { + pragma[only_bind_out](apa) = pragma[only_bind_out](result) + } - bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, + Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, + pragma[only_bind_into](config)) and + matchesCall(ccc, call) + } - pragma[inline] - private Content getHeadContent(Ap ap) { exists(result) and ap = true } + /** + * Holds if `node` is reachable with access path `ap` from a source in the + * configuration `config`. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. + */ + pragma[nomagic] + predicate fwdFlow( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + fwdFlow0(node, state, cc, argAp, ap, config) and + PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and + filter(node, state, ap, config) + } - class ApOption = BooleanOption; + pragma[nomagic] + private predicate fwdFlow0( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + sourceNode(node, state, config) and + (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and + argAp = apNone() and + ap = getApNil(node) + or + exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | + fwdFlow(mid, state0, cc, argAp, ap0, config) and + localCc = getLocalCc(mid, cc) + | + localStep(mid, state0, node, state, true, _, config, localCc) and + ap = ap0 + or + localStep(mid, state0, node, state, false, ap, config, localCc) and + ap0 instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + jumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStateStep(mid, state0, node, state, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + // store + exists(TypedContent tc, Ap ap0 | + fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + ap = apCons(tc, ap0) + ) + or + // read + exists(Ap ap0, Content c | + fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowConsCand(ap0, c, ap, config) + ) + or + // flow into a callable + exists(ApApprox apa | + fwdFlowIn(_, node, state, _, cc, _, ap, config) and + apa = getApprox(ap) and + if PrevStage::parameterMayFlowThrough(node, _, apa, config) + then argAp = apSome(ap) + else argAp = apNone() + ) + or + // flow out of a callable + fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) + ) + } - ApOption apNone() { result = TBooleanNone() } + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + exists(DataFlowType contentType | + fwdFlow(node1, state, cc, argAp, ap1, config) and + PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and + typecheckStore(ap1, contentType) + ) + } - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + /** + * Holds if forward flow with access path `tail` reaches a store of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(TypedContent tc | + fwdFlowStore(_, tail, tc, _, _, _, _, config) and + tc.getContent() = c and + cons = apCons(tc, tail) + ) + } + pragma[nomagic] + private predicate fwdFlowRead( + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + fwdFlow(node1, state, cc, argAp, ap, config) and + PrevStage::readStepCand(node1, c, node2, config) and + getHeadContent(ap) = c + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, + Ap ap, Configuration config + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, state, outercc, argAp, ap, config) and + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutNotFromArg( + NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + ) { + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, argAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + ccOut = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + ) + } + + pragma[nomagic] + private predicate storeStepFwd( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config + ) { + fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + ap2 = apCons(tc, ap1) and + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + } + + private predicate readStepFwd( + NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config + ) { + fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowConsCand(ap1, c, ap2, config) + } + + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and + fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), + pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + + pragma[nomagic] + private predicate returnNodeMayFlowThrough( + RetNodeEx ret, FlowState state, Ap ap, Configuration config + ) { + fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from the + * enclosing callable in order to reach a sink, and if so, `returnAp` records + * the access path of the returned value. + */ + pragma[nomagic] + predicate revFlow( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + revFlow0(node, state, toReturn, returnAp, ap, config) and + fwdFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + fwdFlow(node, state, _, _, ap, config) and + sinkNode(node, state, config) and + (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid, FlowState state0 | + localStep(node, state, mid, state0, true, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, ap, config) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, state, _, _, ap, config) and + toReturn = false and + returnAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStep(node, mid, config) and + revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStateStep(node, state, mid, state0, config) and + revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, + pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowConsCand(ap0, c, ap, config) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, state, toReturn, returnAp, ap0, config) and + readStepFwd(node, ap, _, mid, ap0, config) + ) + or + // flow into a callable + revFlowInNotToReturn(node, state, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + or + // flow out of a callable + revFlowOut(_, node, state, _, _, ap, config) and + toReturn = true and + if returnNodeMayFlowThrough(node, state, ap, config) + then returnAp = apSome(ap) + else returnAp = apNone() + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, + boolean toReturn, ApOption returnAp, Configuration config + ) { + revFlow(mid, state, toReturn, returnAp, ap0, config) and + storeStepFwd(node, ap, tc, mid, ap0, config) and + tc.getContent() = c + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, _, tail, config) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0, config) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, + Configuration config + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, state, toReturn, returnAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInNotToReturn( + ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, FlowState state, CcCall ccc | + revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and + fwdFlow(ret, state, ccc, apSome(_), ap, config) and + matchesCall(ccc, call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Ap ap2, Content c | + PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and + revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and + revFlowConsCand(ap2, c, ap1, config) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and + readStepFwd(node1, ap1, c, node2, ap2, config) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, + pragma[only_bind_into](config)) + ) + } + + predicate revFlow(NodeEx node, FlowState state, Configuration config) { + revFlow(node, state, _, _, _, config) + } + + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, ap, config) + } + + private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepFwd(_, ap, tc, _, _, config) + } + + private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepCand(_, ap, tc, _, _, config) + } + + private predicate validAp(Ap ap, Configuration config) { + revFlow(_, _, _, _, ap, config) and ap instanceof ApNil + or + exists(TypedContent head, Ap tail | + consCand(head, tail, config) and + ap = apCons(head, tail) + ) + } + + predicate consCand(TypedContent tc, Ap ap, Configuration config) { + revConsCand(tc, ap, config) and + validAp(ap, config) + } + + pragma[noinline] + private predicate parameterFlow( + ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ) { + revFlow(p, _, true, apSome(ap0), ap, config) and + c = p.getEnclosingCallable() + } + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | + parameterFlow(p, ap, ap0, c, config) and + c = ret.getEnclosingCallable() and + revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), + pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and + fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and + kind = ret.getKind() and + p.getPosition() = pos and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists( + Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + | + revFlow(arg, state, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + + predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, argAp, ap, config) + ) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | consCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and + states = count(FlowState state | revFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | + revFlow(n, state, b, retAp, ap, config) + ) + } + /* End: Stage logic. */ + } +} + +private module BooleanCallContext { + class Cc extends boolean { + Cc() { this in [true, false] } + } + + class CcCall extends Cc { + CcCall() { this = true } + } + + /** Holds if the call context may be `call`. */ + predicate matchesCall(CcCall cc, DataFlowCall call) { any() } + + class CcNoCall extends Cc { + CcNoCall() { this = false } + } + + Cc ccNone() { result = false } + + CcCall ccSomeCall() { result = true } + + class LocalCc = Unit; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } +} + +private module Level1CallContext { class Cc = CallContext; class CcCall = CallContextCall; + pragma[inline] + predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } + class CcNoCall = CallContextNoCall; Cc ccNone() { result instanceof CallContextAny } CcCall ccSomeCall() { result instanceof CallContextSomeCall } - private class LocalCc = Unit; + module NoLocalCallContext { + class LocalCc = Unit; - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSiteDispatch(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSiteDispatch(call, c) + then result = TSpecificCall(call) + else result = TSomeCall() + } + } + + module LocalCallContext { + class LocalCc = LocalCallContext; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { + result = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + node.getEnclosingCallable()) + } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() + } } bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { checkCallContextReturn(innercc, c, call) and if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() } +} - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } +private module Stage2Param implements MkStage::StageParam { + private module PrevStage = Stage1; + + class Ap extends boolean { + Ap() { this in [true, false] } + } + + class ApNil extends Ap { + ApNil() { this = false } + } + + bindingset[result, ap] + PrevStage::Ap getApprox(Ap ap) { any() } + + ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + + pragma[inline] + Content getHeadContent(Ap ap) { exists(result) and ap = true } + + class ApOption = BooleanOption; + + ApOption apNone() { result = TBooleanNone() } + + ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + + import Level1CallContext + import NoLocalCallContext bindingset[node1, state1, config] bindingset[node2, state2, config] - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -1221,9 +1906,9 @@ private module Stage2 { exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/5; - private predicate flowIntoCall = flowIntoCallNodeCand1/5; + predicate flowIntoCall = flowIntoCallNodeCand1/5; pragma[nomagic] private predicate expectsContentCand(NodeEx node, Configuration config) { @@ -1235,7 +1920,7 @@ private module Stage2 { } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and not stateBarrier(node, state, config) and @@ -1248,544 +1933,11 @@ private module Stage2 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 2 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 2 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage2 = MkStage::Stage; + pragma[nomagic] private predicate flowOutOfCallNodeCand2( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config @@ -1883,14 +2035,13 @@ private module LocalFlowBigStep { ) { additionalLocalFlowStepNodeCand1(node1, node2, config) and state1 = state2 and - Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false, - pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false, + Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false, pragma[only_bind_into](config)) or additionalLocalStateStep(node1, state1, node2, state2, config) and - Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config)) + Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config)) } /** @@ -1967,26 +2118,24 @@ private module LocalFlowBigStep { private import LocalFlowBigStep -private module Stage3 { - module PrevStage = Stage2; - - class ApApprox = PrevStage::Ap; +private module Stage3Param implements MkStage::StageParam { + private module PrevStage = Stage2; class Ap = AccessPathFront; class ApNil = AccessPathFrontNil; - private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } + PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } + Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathFrontOption; @@ -1994,44 +2143,18 @@ private module Stage3 { ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - class Cc = boolean; + import BooleanCallContext - class CcCall extends Cc { - CcCall() { this = true } - - /** Holds if this call context may be `call`. */ - predicate matchesCall(DataFlowCall call) { any() } - } - - class CcNoCall extends Cc { - CcNoCall() { this = false } - } - - Cc ccNone() { result = false } - - CcCall ccSomeCall() { result = true } - - private class LocalCc = Unit; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/5; - private predicate flowIntoCall = flowIntoCallNodeCand2/5; + predicate flowIntoCall = flowIntoCallNodeCand2/5; pragma[nomagic] private predicate clearSet(NodeEx node, ContentSet c, Configuration config) { @@ -2067,7 +2190,7 @@ private module Stage3 { private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { exists(state) and exists(config) and not clear(node, ap, config) and @@ -2080,548 +2203,15 @@ private module Stage3 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { + predicate typecheckStore(Ap ap, DataFlowType contentType) { // We need to typecheck stores here, since reverse flow through a getter // might have a different type here compared to inside the getter. compatibleTypes(ap.getType(), contentType) } - - /* Begin: Stage 3 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 3 logic. */ } +private module Stage3 = MkStage::Stage; + /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` * and remains relevant for the following pruning stage. @@ -2644,7 +2234,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and nodes = strictcount(NodeEx n, FlowState state | - Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) or flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) ) and @@ -2828,26 +2418,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } } -private module Stage4 { - module PrevStage = Stage3; - - class ApApprox = PrevStage::Ap; +private module Stage4Param implements MkStage::StageParam { + private module PrevStage = Stage3; class Ap = AccessPathApprox; class ApNil = AccessPathApproxNil; - private ApApprox getApprox(Ap ap) { result = ap.getFront() } + PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } + Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathApproxOption; @@ -2855,38 +2443,10 @@ private module Stage4 { ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - class Cc = CallContext; + import Level1CallContext + import LocalCallContext - class CcCall = CallContextCall; - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - private class LocalCc = LocalCallContext; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() - } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { - result = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - node.getEnclosingCallable()) - } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -2894,575 +2454,40 @@ private module Stage4 { } pragma[nomagic] - private predicate flowOutOfCall( + predicate flowOutOfCall( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } pragma[nomagic] - private predicate flowIntoCall( + predicate flowIntoCall( DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } // Type checking is not necessary here as it has already been done in stage 3. bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 4 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 4 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage4 = MkStage::Stage; + bindingset[conf, result] private Configuration unbindConf(Configuration conf) { exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) @@ -3495,7 +2520,7 @@ private newtype TSummaryCtx = TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and - Stage4::revFlow(p, state, _, _, _, config) + Stage4::revFlow(p, state, _, config) ) } @@ -3553,7 +2578,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { result = strictcount(NodeEx n, FlowState state | - Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config) + Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config) ) } @@ -3667,7 +2692,7 @@ private newtype TPathNode = exists(PathNodeMid mid | pathStep(mid, node, state, cc, sc, ap) and pragma[only_bind_into](config) = mid.getConfiguration() and - Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config)) + Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config)) ) } or TPathNodeSink(NodeEx node, FlowState state, Configuration config) { @@ -4207,7 +3232,7 @@ private NodeEx getAnOutNodeFlow( ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result.asNode() = kind.getAnOutNode(call) and - Stage4::revFlow(result, _, _, _, apa, config) + Stage4::revFlow(result, _, apa, config) } /** @@ -4243,7 +3268,7 @@ private predicate parameterCand( DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config ) { exists(ParamNodeEx p | - Stage4::revFlow(p, _, _, _, apa, config) and + Stage4::revFlow(p, _, apa, config) and p.isParameterOf(callable, pos) ) } diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll index a076f7a2e45..22c3bd2466e 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,12 +944,9 @@ private module Stage1 { predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) } bindingset[node, state, config] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, toReturn, pragma[only_bind_into](config)) and + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, _, pragma[only_bind_into](config)) and exists(state) and - exists(returnAp) and exists(ap) } @@ -1142,66 +1139,754 @@ private predicate flowIntoCallNodeCand1( ) } -private module Stage2 { - module PrevStage = Stage1; +private signature module StageSig { + class Ap; + predicate revFlow(NodeEx node, Configuration config); + + bindingset[node, state, config] + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config); + + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ); + + predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config); +} + +private module MkStage { class ApApprox = PrevStage::Ap; - class Ap = boolean; + signature module StageParam { + class Ap; - class ApNil extends Ap { - ApNil() { this = false } + class ApNil extends Ap; + + bindingset[result, ap] + ApApprox getApprox(Ap ap); + + ApNil getApNil(NodeEx node); + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail); + + Content getHeadContent(Ap ap); + + class ApOption; + + ApOption apNone(); + + ApOption apSome(Ap ap); + + class Cc; + + class CcCall extends Cc; + + // TODO: member predicate on CcCall + predicate matchesCall(CcCall cc, DataFlowCall call); + + class CcNoCall extends Cc; + + Cc ccNone(); + + CcCall ccSomeCall(); + + class LocalCc; + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc); + + bindingset[node1, state1, config] + bindingset[node2, state2, config] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ); + + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + ); + + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ); + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config); + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType); } - bindingset[result, ap] - private ApApprox getApprox(Ap ap) { any() } + module Stage implements StageSig { + import Param - private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) } + /* Begin: Stage logic. */ + bindingset[result, apa] + private ApApprox unbindApa(ApApprox apa) { + pragma[only_bind_out](apa) = pragma[only_bind_out](result) + } - bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, + Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, + pragma[only_bind_into](config)) and + matchesCall(ccc, call) + } - pragma[inline] - private Content getHeadContent(Ap ap) { exists(result) and ap = true } + /** + * Holds if `node` is reachable with access path `ap` from a source in the + * configuration `config`. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. + */ + pragma[nomagic] + predicate fwdFlow( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + fwdFlow0(node, state, cc, argAp, ap, config) and + PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and + filter(node, state, ap, config) + } - class ApOption = BooleanOption; + pragma[nomagic] + private predicate fwdFlow0( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + sourceNode(node, state, config) and + (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and + argAp = apNone() and + ap = getApNil(node) + or + exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | + fwdFlow(mid, state0, cc, argAp, ap0, config) and + localCc = getLocalCc(mid, cc) + | + localStep(mid, state0, node, state, true, _, config, localCc) and + ap = ap0 + or + localStep(mid, state0, node, state, false, ap, config, localCc) and + ap0 instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + jumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStateStep(mid, state0, node, state, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + // store + exists(TypedContent tc, Ap ap0 | + fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + ap = apCons(tc, ap0) + ) + or + // read + exists(Ap ap0, Content c | + fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowConsCand(ap0, c, ap, config) + ) + or + // flow into a callable + exists(ApApprox apa | + fwdFlowIn(_, node, state, _, cc, _, ap, config) and + apa = getApprox(ap) and + if PrevStage::parameterMayFlowThrough(node, _, apa, config) + then argAp = apSome(ap) + else argAp = apNone() + ) + or + // flow out of a callable + fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) + ) + } - ApOption apNone() { result = TBooleanNone() } + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + exists(DataFlowType contentType | + fwdFlow(node1, state, cc, argAp, ap1, config) and + PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and + typecheckStore(ap1, contentType) + ) + } - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + /** + * Holds if forward flow with access path `tail` reaches a store of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(TypedContent tc | + fwdFlowStore(_, tail, tc, _, _, _, _, config) and + tc.getContent() = c and + cons = apCons(tc, tail) + ) + } + pragma[nomagic] + private predicate fwdFlowRead( + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + fwdFlow(node1, state, cc, argAp, ap, config) and + PrevStage::readStepCand(node1, c, node2, config) and + getHeadContent(ap) = c + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, + Ap ap, Configuration config + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, state, outercc, argAp, ap, config) and + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutNotFromArg( + NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + ) { + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, argAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + ccOut = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + ) + } + + pragma[nomagic] + private predicate storeStepFwd( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config + ) { + fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + ap2 = apCons(tc, ap1) and + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + } + + private predicate readStepFwd( + NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config + ) { + fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowConsCand(ap1, c, ap2, config) + } + + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and + fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), + pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + + pragma[nomagic] + private predicate returnNodeMayFlowThrough( + RetNodeEx ret, FlowState state, Ap ap, Configuration config + ) { + fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from the + * enclosing callable in order to reach a sink, and if so, `returnAp` records + * the access path of the returned value. + */ + pragma[nomagic] + predicate revFlow( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + revFlow0(node, state, toReturn, returnAp, ap, config) and + fwdFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + fwdFlow(node, state, _, _, ap, config) and + sinkNode(node, state, config) and + (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid, FlowState state0 | + localStep(node, state, mid, state0, true, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, ap, config) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, state, _, _, ap, config) and + toReturn = false and + returnAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStep(node, mid, config) and + revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStateStep(node, state, mid, state0, config) and + revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, + pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowConsCand(ap0, c, ap, config) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, state, toReturn, returnAp, ap0, config) and + readStepFwd(node, ap, _, mid, ap0, config) + ) + or + // flow into a callable + revFlowInNotToReturn(node, state, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + or + // flow out of a callable + revFlowOut(_, node, state, _, _, ap, config) and + toReturn = true and + if returnNodeMayFlowThrough(node, state, ap, config) + then returnAp = apSome(ap) + else returnAp = apNone() + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, + boolean toReturn, ApOption returnAp, Configuration config + ) { + revFlow(mid, state, toReturn, returnAp, ap0, config) and + storeStepFwd(node, ap, tc, mid, ap0, config) and + tc.getContent() = c + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, _, tail, config) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0, config) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, + Configuration config + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, state, toReturn, returnAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInNotToReturn( + ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, FlowState state, CcCall ccc | + revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and + fwdFlow(ret, state, ccc, apSome(_), ap, config) and + matchesCall(ccc, call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Ap ap2, Content c | + PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and + revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and + revFlowConsCand(ap2, c, ap1, config) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and + readStepFwd(node1, ap1, c, node2, ap2, config) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, + pragma[only_bind_into](config)) + ) + } + + predicate revFlow(NodeEx node, FlowState state, Configuration config) { + revFlow(node, state, _, _, _, config) + } + + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, ap, config) + } + + private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepFwd(_, ap, tc, _, _, config) + } + + private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepCand(_, ap, tc, _, _, config) + } + + private predicate validAp(Ap ap, Configuration config) { + revFlow(_, _, _, _, ap, config) and ap instanceof ApNil + or + exists(TypedContent head, Ap tail | + consCand(head, tail, config) and + ap = apCons(head, tail) + ) + } + + predicate consCand(TypedContent tc, Ap ap, Configuration config) { + revConsCand(tc, ap, config) and + validAp(ap, config) + } + + pragma[noinline] + private predicate parameterFlow( + ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ) { + revFlow(p, _, true, apSome(ap0), ap, config) and + c = p.getEnclosingCallable() + } + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | + parameterFlow(p, ap, ap0, c, config) and + c = ret.getEnclosingCallable() and + revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), + pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and + fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and + kind = ret.getKind() and + p.getPosition() = pos and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists( + Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + | + revFlow(arg, state, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + + predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, argAp, ap, config) + ) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | consCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and + states = count(FlowState state | revFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | + revFlow(n, state, b, retAp, ap, config) + ) + } + /* End: Stage logic. */ + } +} + +private module BooleanCallContext { + class Cc extends boolean { + Cc() { this in [true, false] } + } + + class CcCall extends Cc { + CcCall() { this = true } + } + + /** Holds if the call context may be `call`. */ + predicate matchesCall(CcCall cc, DataFlowCall call) { any() } + + class CcNoCall extends Cc { + CcNoCall() { this = false } + } + + Cc ccNone() { result = false } + + CcCall ccSomeCall() { result = true } + + class LocalCc = Unit; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } +} + +private module Level1CallContext { class Cc = CallContext; class CcCall = CallContextCall; + pragma[inline] + predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } + class CcNoCall = CallContextNoCall; Cc ccNone() { result instanceof CallContextAny } CcCall ccSomeCall() { result instanceof CallContextSomeCall } - private class LocalCc = Unit; + module NoLocalCallContext { + class LocalCc = Unit; - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSiteDispatch(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSiteDispatch(call, c) + then result = TSpecificCall(call) + else result = TSomeCall() + } + } + + module LocalCallContext { + class LocalCc = LocalCallContext; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { + result = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + node.getEnclosingCallable()) + } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() + } } bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { checkCallContextReturn(innercc, c, call) and if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() } +} - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } +private module Stage2Param implements MkStage::StageParam { + private module PrevStage = Stage1; + + class Ap extends boolean { + Ap() { this in [true, false] } + } + + class ApNil extends Ap { + ApNil() { this = false } + } + + bindingset[result, ap] + PrevStage::Ap getApprox(Ap ap) { any() } + + ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + + pragma[inline] + Content getHeadContent(Ap ap) { exists(result) and ap = true } + + class ApOption = BooleanOption; + + ApOption apNone() { result = TBooleanNone() } + + ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + + import Level1CallContext + import NoLocalCallContext bindingset[node1, state1, config] bindingset[node2, state2, config] - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -1221,9 +1906,9 @@ private module Stage2 { exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/5; - private predicate flowIntoCall = flowIntoCallNodeCand1/5; + predicate flowIntoCall = flowIntoCallNodeCand1/5; pragma[nomagic] private predicate expectsContentCand(NodeEx node, Configuration config) { @@ -1235,7 +1920,7 @@ private module Stage2 { } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and not stateBarrier(node, state, config) and @@ -1248,544 +1933,11 @@ private module Stage2 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 2 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 2 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage2 = MkStage::Stage; + pragma[nomagic] private predicate flowOutOfCallNodeCand2( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config @@ -1883,14 +2035,13 @@ private module LocalFlowBigStep { ) { additionalLocalFlowStepNodeCand1(node1, node2, config) and state1 = state2 and - Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false, - pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false, + Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false, pragma[only_bind_into](config)) or additionalLocalStateStep(node1, state1, node2, state2, config) and - Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config)) + Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config)) } /** @@ -1967,26 +2118,24 @@ private module LocalFlowBigStep { private import LocalFlowBigStep -private module Stage3 { - module PrevStage = Stage2; - - class ApApprox = PrevStage::Ap; +private module Stage3Param implements MkStage::StageParam { + private module PrevStage = Stage2; class Ap = AccessPathFront; class ApNil = AccessPathFrontNil; - private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } + PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } + Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathFrontOption; @@ -1994,44 +2143,18 @@ private module Stage3 { ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - class Cc = boolean; + import BooleanCallContext - class CcCall extends Cc { - CcCall() { this = true } - - /** Holds if this call context may be `call`. */ - predicate matchesCall(DataFlowCall call) { any() } - } - - class CcNoCall extends Cc { - CcNoCall() { this = false } - } - - Cc ccNone() { result = false } - - CcCall ccSomeCall() { result = true } - - private class LocalCc = Unit; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/5; - private predicate flowIntoCall = flowIntoCallNodeCand2/5; + predicate flowIntoCall = flowIntoCallNodeCand2/5; pragma[nomagic] private predicate clearSet(NodeEx node, ContentSet c, Configuration config) { @@ -2067,7 +2190,7 @@ private module Stage3 { private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { exists(state) and exists(config) and not clear(node, ap, config) and @@ -2080,548 +2203,15 @@ private module Stage3 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { + predicate typecheckStore(Ap ap, DataFlowType contentType) { // We need to typecheck stores here, since reverse flow through a getter // might have a different type here compared to inside the getter. compatibleTypes(ap.getType(), contentType) } - - /* Begin: Stage 3 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 3 logic. */ } +private module Stage3 = MkStage::Stage; + /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` * and remains relevant for the following pruning stage. @@ -2644,7 +2234,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and nodes = strictcount(NodeEx n, FlowState state | - Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) or flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) ) and @@ -2828,26 +2418,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } } -private module Stage4 { - module PrevStage = Stage3; - - class ApApprox = PrevStage::Ap; +private module Stage4Param implements MkStage::StageParam { + private module PrevStage = Stage3; class Ap = AccessPathApprox; class ApNil = AccessPathApproxNil; - private ApApprox getApprox(Ap ap) { result = ap.getFront() } + PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } + Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathApproxOption; @@ -2855,38 +2443,10 @@ private module Stage4 { ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - class Cc = CallContext; + import Level1CallContext + import LocalCallContext - class CcCall = CallContextCall; - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - private class LocalCc = LocalCallContext; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() - } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { - result = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - node.getEnclosingCallable()) - } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -2894,575 +2454,40 @@ private module Stage4 { } pragma[nomagic] - private predicate flowOutOfCall( + predicate flowOutOfCall( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } pragma[nomagic] - private predicate flowIntoCall( + predicate flowIntoCall( DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } // Type checking is not necessary here as it has already been done in stage 3. bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 4 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 4 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage4 = MkStage::Stage; + bindingset[conf, result] private Configuration unbindConf(Configuration conf) { exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) @@ -3495,7 +2520,7 @@ private newtype TSummaryCtx = TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and - Stage4::revFlow(p, state, _, _, _, config) + Stage4::revFlow(p, state, _, config) ) } @@ -3553,7 +2578,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { result = strictcount(NodeEx n, FlowState state | - Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config) + Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config) ) } @@ -3667,7 +2692,7 @@ private newtype TPathNode = exists(PathNodeMid mid | pathStep(mid, node, state, cc, sc, ap) and pragma[only_bind_into](config) = mid.getConfiguration() and - Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config)) + Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config)) ) } or TPathNodeSink(NodeEx node, FlowState state, Configuration config) { @@ -4207,7 +3232,7 @@ private NodeEx getAnOutNodeFlow( ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result.asNode() = kind.getAnOutNode(call) and - Stage4::revFlow(result, _, _, _, apa, config) + Stage4::revFlow(result, _, apa, config) } /** @@ -4243,7 +3268,7 @@ private predicate parameterCand( DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config ) { exists(ParamNodeEx p | - Stage4::revFlow(p, _, _, _, apa, config) and + Stage4::revFlow(p, _, apa, config) and p.isParameterOf(callable, pos) ) } diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll index a076f7a2e45..22c3bd2466e 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,12 +944,9 @@ private module Stage1 { predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) } bindingset[node, state, config] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, toReturn, pragma[only_bind_into](config)) and + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, _, pragma[only_bind_into](config)) and exists(state) and - exists(returnAp) and exists(ap) } @@ -1142,66 +1139,754 @@ private predicate flowIntoCallNodeCand1( ) } -private module Stage2 { - module PrevStage = Stage1; +private signature module StageSig { + class Ap; + predicate revFlow(NodeEx node, Configuration config); + + bindingset[node, state, config] + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config); + + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ); + + predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config); +} + +private module MkStage { class ApApprox = PrevStage::Ap; - class Ap = boolean; + signature module StageParam { + class Ap; - class ApNil extends Ap { - ApNil() { this = false } + class ApNil extends Ap; + + bindingset[result, ap] + ApApprox getApprox(Ap ap); + + ApNil getApNil(NodeEx node); + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail); + + Content getHeadContent(Ap ap); + + class ApOption; + + ApOption apNone(); + + ApOption apSome(Ap ap); + + class Cc; + + class CcCall extends Cc; + + // TODO: member predicate on CcCall + predicate matchesCall(CcCall cc, DataFlowCall call); + + class CcNoCall extends Cc; + + Cc ccNone(); + + CcCall ccSomeCall(); + + class LocalCc; + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc); + + bindingset[node1, state1, config] + bindingset[node2, state2, config] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ); + + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + ); + + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ); + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config); + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType); } - bindingset[result, ap] - private ApApprox getApprox(Ap ap) { any() } + module Stage implements StageSig { + import Param - private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) } + /* Begin: Stage logic. */ + bindingset[result, apa] + private ApApprox unbindApa(ApApprox apa) { + pragma[only_bind_out](apa) = pragma[only_bind_out](result) + } - bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, + Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, + pragma[only_bind_into](config)) and + matchesCall(ccc, call) + } - pragma[inline] - private Content getHeadContent(Ap ap) { exists(result) and ap = true } + /** + * Holds if `node` is reachable with access path `ap` from a source in the + * configuration `config`. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. + */ + pragma[nomagic] + predicate fwdFlow( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + fwdFlow0(node, state, cc, argAp, ap, config) and + PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and + filter(node, state, ap, config) + } - class ApOption = BooleanOption; + pragma[nomagic] + private predicate fwdFlow0( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + sourceNode(node, state, config) and + (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and + argAp = apNone() and + ap = getApNil(node) + or + exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | + fwdFlow(mid, state0, cc, argAp, ap0, config) and + localCc = getLocalCc(mid, cc) + | + localStep(mid, state0, node, state, true, _, config, localCc) and + ap = ap0 + or + localStep(mid, state0, node, state, false, ap, config, localCc) and + ap0 instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + jumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStateStep(mid, state0, node, state, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + // store + exists(TypedContent tc, Ap ap0 | + fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + ap = apCons(tc, ap0) + ) + or + // read + exists(Ap ap0, Content c | + fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowConsCand(ap0, c, ap, config) + ) + or + // flow into a callable + exists(ApApprox apa | + fwdFlowIn(_, node, state, _, cc, _, ap, config) and + apa = getApprox(ap) and + if PrevStage::parameterMayFlowThrough(node, _, apa, config) + then argAp = apSome(ap) + else argAp = apNone() + ) + or + // flow out of a callable + fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) + ) + } - ApOption apNone() { result = TBooleanNone() } + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + exists(DataFlowType contentType | + fwdFlow(node1, state, cc, argAp, ap1, config) and + PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and + typecheckStore(ap1, contentType) + ) + } - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + /** + * Holds if forward flow with access path `tail` reaches a store of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(TypedContent tc | + fwdFlowStore(_, tail, tc, _, _, _, _, config) and + tc.getContent() = c and + cons = apCons(tc, tail) + ) + } + pragma[nomagic] + private predicate fwdFlowRead( + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + fwdFlow(node1, state, cc, argAp, ap, config) and + PrevStage::readStepCand(node1, c, node2, config) and + getHeadContent(ap) = c + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, + Ap ap, Configuration config + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, state, outercc, argAp, ap, config) and + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutNotFromArg( + NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + ) { + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, argAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + ccOut = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + ) + } + + pragma[nomagic] + private predicate storeStepFwd( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config + ) { + fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + ap2 = apCons(tc, ap1) and + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + } + + private predicate readStepFwd( + NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config + ) { + fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowConsCand(ap1, c, ap2, config) + } + + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and + fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), + pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + + pragma[nomagic] + private predicate returnNodeMayFlowThrough( + RetNodeEx ret, FlowState state, Ap ap, Configuration config + ) { + fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from the + * enclosing callable in order to reach a sink, and if so, `returnAp` records + * the access path of the returned value. + */ + pragma[nomagic] + predicate revFlow( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + revFlow0(node, state, toReturn, returnAp, ap, config) and + fwdFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + fwdFlow(node, state, _, _, ap, config) and + sinkNode(node, state, config) and + (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid, FlowState state0 | + localStep(node, state, mid, state0, true, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, ap, config) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, state, _, _, ap, config) and + toReturn = false and + returnAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStep(node, mid, config) and + revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStateStep(node, state, mid, state0, config) and + revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, + pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowConsCand(ap0, c, ap, config) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, state, toReturn, returnAp, ap0, config) and + readStepFwd(node, ap, _, mid, ap0, config) + ) + or + // flow into a callable + revFlowInNotToReturn(node, state, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + or + // flow out of a callable + revFlowOut(_, node, state, _, _, ap, config) and + toReturn = true and + if returnNodeMayFlowThrough(node, state, ap, config) + then returnAp = apSome(ap) + else returnAp = apNone() + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, + boolean toReturn, ApOption returnAp, Configuration config + ) { + revFlow(mid, state, toReturn, returnAp, ap0, config) and + storeStepFwd(node, ap, tc, mid, ap0, config) and + tc.getContent() = c + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, _, tail, config) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0, config) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, + Configuration config + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, state, toReturn, returnAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInNotToReturn( + ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, FlowState state, CcCall ccc | + revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and + fwdFlow(ret, state, ccc, apSome(_), ap, config) and + matchesCall(ccc, call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Ap ap2, Content c | + PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and + revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and + revFlowConsCand(ap2, c, ap1, config) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and + readStepFwd(node1, ap1, c, node2, ap2, config) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, + pragma[only_bind_into](config)) + ) + } + + predicate revFlow(NodeEx node, FlowState state, Configuration config) { + revFlow(node, state, _, _, _, config) + } + + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, ap, config) + } + + private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepFwd(_, ap, tc, _, _, config) + } + + private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepCand(_, ap, tc, _, _, config) + } + + private predicate validAp(Ap ap, Configuration config) { + revFlow(_, _, _, _, ap, config) and ap instanceof ApNil + or + exists(TypedContent head, Ap tail | + consCand(head, tail, config) and + ap = apCons(head, tail) + ) + } + + predicate consCand(TypedContent tc, Ap ap, Configuration config) { + revConsCand(tc, ap, config) and + validAp(ap, config) + } + + pragma[noinline] + private predicate parameterFlow( + ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ) { + revFlow(p, _, true, apSome(ap0), ap, config) and + c = p.getEnclosingCallable() + } + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | + parameterFlow(p, ap, ap0, c, config) and + c = ret.getEnclosingCallable() and + revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), + pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and + fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and + kind = ret.getKind() and + p.getPosition() = pos and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists( + Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + | + revFlow(arg, state, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + + predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, argAp, ap, config) + ) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | consCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and + states = count(FlowState state | revFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | + revFlow(n, state, b, retAp, ap, config) + ) + } + /* End: Stage logic. */ + } +} + +private module BooleanCallContext { + class Cc extends boolean { + Cc() { this in [true, false] } + } + + class CcCall extends Cc { + CcCall() { this = true } + } + + /** Holds if the call context may be `call`. */ + predicate matchesCall(CcCall cc, DataFlowCall call) { any() } + + class CcNoCall extends Cc { + CcNoCall() { this = false } + } + + Cc ccNone() { result = false } + + CcCall ccSomeCall() { result = true } + + class LocalCc = Unit; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } +} + +private module Level1CallContext { class Cc = CallContext; class CcCall = CallContextCall; + pragma[inline] + predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } + class CcNoCall = CallContextNoCall; Cc ccNone() { result instanceof CallContextAny } CcCall ccSomeCall() { result instanceof CallContextSomeCall } - private class LocalCc = Unit; + module NoLocalCallContext { + class LocalCc = Unit; - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSiteDispatch(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSiteDispatch(call, c) + then result = TSpecificCall(call) + else result = TSomeCall() + } + } + + module LocalCallContext { + class LocalCc = LocalCallContext; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { + result = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + node.getEnclosingCallable()) + } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() + } } bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { checkCallContextReturn(innercc, c, call) and if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() } +} - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } +private module Stage2Param implements MkStage::StageParam { + private module PrevStage = Stage1; + + class Ap extends boolean { + Ap() { this in [true, false] } + } + + class ApNil extends Ap { + ApNil() { this = false } + } + + bindingset[result, ap] + PrevStage::Ap getApprox(Ap ap) { any() } + + ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + + pragma[inline] + Content getHeadContent(Ap ap) { exists(result) and ap = true } + + class ApOption = BooleanOption; + + ApOption apNone() { result = TBooleanNone() } + + ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + + import Level1CallContext + import NoLocalCallContext bindingset[node1, state1, config] bindingset[node2, state2, config] - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -1221,9 +1906,9 @@ private module Stage2 { exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/5; - private predicate flowIntoCall = flowIntoCallNodeCand1/5; + predicate flowIntoCall = flowIntoCallNodeCand1/5; pragma[nomagic] private predicate expectsContentCand(NodeEx node, Configuration config) { @@ -1235,7 +1920,7 @@ private module Stage2 { } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and not stateBarrier(node, state, config) and @@ -1248,544 +1933,11 @@ private module Stage2 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 2 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 2 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage2 = MkStage::Stage; + pragma[nomagic] private predicate flowOutOfCallNodeCand2( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config @@ -1883,14 +2035,13 @@ private module LocalFlowBigStep { ) { additionalLocalFlowStepNodeCand1(node1, node2, config) and state1 = state2 and - Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false, - pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false, + Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false, pragma[only_bind_into](config)) or additionalLocalStateStep(node1, state1, node2, state2, config) and - Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config)) + Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config)) } /** @@ -1967,26 +2118,24 @@ private module LocalFlowBigStep { private import LocalFlowBigStep -private module Stage3 { - module PrevStage = Stage2; - - class ApApprox = PrevStage::Ap; +private module Stage3Param implements MkStage::StageParam { + private module PrevStage = Stage2; class Ap = AccessPathFront; class ApNil = AccessPathFrontNil; - private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } + PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } + Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathFrontOption; @@ -1994,44 +2143,18 @@ private module Stage3 { ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - class Cc = boolean; + import BooleanCallContext - class CcCall extends Cc { - CcCall() { this = true } - - /** Holds if this call context may be `call`. */ - predicate matchesCall(DataFlowCall call) { any() } - } - - class CcNoCall extends Cc { - CcNoCall() { this = false } - } - - Cc ccNone() { result = false } - - CcCall ccSomeCall() { result = true } - - private class LocalCc = Unit; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/5; - private predicate flowIntoCall = flowIntoCallNodeCand2/5; + predicate flowIntoCall = flowIntoCallNodeCand2/5; pragma[nomagic] private predicate clearSet(NodeEx node, ContentSet c, Configuration config) { @@ -2067,7 +2190,7 @@ private module Stage3 { private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { exists(state) and exists(config) and not clear(node, ap, config) and @@ -2080,548 +2203,15 @@ private module Stage3 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { + predicate typecheckStore(Ap ap, DataFlowType contentType) { // We need to typecheck stores here, since reverse flow through a getter // might have a different type here compared to inside the getter. compatibleTypes(ap.getType(), contentType) } - - /* Begin: Stage 3 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 3 logic. */ } +private module Stage3 = MkStage::Stage; + /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` * and remains relevant for the following pruning stage. @@ -2644,7 +2234,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and nodes = strictcount(NodeEx n, FlowState state | - Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) or flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) ) and @@ -2828,26 +2418,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } } -private module Stage4 { - module PrevStage = Stage3; - - class ApApprox = PrevStage::Ap; +private module Stage4Param implements MkStage::StageParam { + private module PrevStage = Stage3; class Ap = AccessPathApprox; class ApNil = AccessPathApproxNil; - private ApApprox getApprox(Ap ap) { result = ap.getFront() } + PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } + Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathApproxOption; @@ -2855,38 +2443,10 @@ private module Stage4 { ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - class Cc = CallContext; + import Level1CallContext + import LocalCallContext - class CcCall = CallContextCall; - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - private class LocalCc = LocalCallContext; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() - } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { - result = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - node.getEnclosingCallable()) - } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -2894,575 +2454,40 @@ private module Stage4 { } pragma[nomagic] - private predicate flowOutOfCall( + predicate flowOutOfCall( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } pragma[nomagic] - private predicate flowIntoCall( + predicate flowIntoCall( DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } // Type checking is not necessary here as it has already been done in stage 3. bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 4 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 4 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage4 = MkStage::Stage; + bindingset[conf, result] private Configuration unbindConf(Configuration conf) { exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) @@ -3495,7 +2520,7 @@ private newtype TSummaryCtx = TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and - Stage4::revFlow(p, state, _, _, _, config) + Stage4::revFlow(p, state, _, config) ) } @@ -3553,7 +2578,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { result = strictcount(NodeEx n, FlowState state | - Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config) + Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config) ) } @@ -3667,7 +2692,7 @@ private newtype TPathNode = exists(PathNodeMid mid | pathStep(mid, node, state, cc, sc, ap) and pragma[only_bind_into](config) = mid.getConfiguration() and - Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config)) + Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config)) ) } or TPathNodeSink(NodeEx node, FlowState state, Configuration config) { @@ -4207,7 +3232,7 @@ private NodeEx getAnOutNodeFlow( ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result.asNode() = kind.getAnOutNode(call) and - Stage4::revFlow(result, _, _, _, apa, config) + Stage4::revFlow(result, _, apa, config) } /** @@ -4243,7 +3268,7 @@ private predicate parameterCand( DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config ) { exists(ParamNodeEx p | - Stage4::revFlow(p, _, _, _, apa, config) and + Stage4::revFlow(p, _, apa, config) and p.isParameterOf(callable, pos) ) } diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll index a076f7a2e45..22c3bd2466e 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,12 +944,9 @@ private module Stage1 { predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) } bindingset[node, state, config] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, toReturn, pragma[only_bind_into](config)) and + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, _, pragma[only_bind_into](config)) and exists(state) and - exists(returnAp) and exists(ap) } @@ -1142,66 +1139,754 @@ private predicate flowIntoCallNodeCand1( ) } -private module Stage2 { - module PrevStage = Stage1; +private signature module StageSig { + class Ap; + predicate revFlow(NodeEx node, Configuration config); + + bindingset[node, state, config] + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config); + + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ); + + predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config); +} + +private module MkStage { class ApApprox = PrevStage::Ap; - class Ap = boolean; + signature module StageParam { + class Ap; - class ApNil extends Ap { - ApNil() { this = false } + class ApNil extends Ap; + + bindingset[result, ap] + ApApprox getApprox(Ap ap); + + ApNil getApNil(NodeEx node); + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail); + + Content getHeadContent(Ap ap); + + class ApOption; + + ApOption apNone(); + + ApOption apSome(Ap ap); + + class Cc; + + class CcCall extends Cc; + + // TODO: member predicate on CcCall + predicate matchesCall(CcCall cc, DataFlowCall call); + + class CcNoCall extends Cc; + + Cc ccNone(); + + CcCall ccSomeCall(); + + class LocalCc; + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc); + + bindingset[node1, state1, config] + bindingset[node2, state2, config] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ); + + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + ); + + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ); + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config); + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType); } - bindingset[result, ap] - private ApApprox getApprox(Ap ap) { any() } + module Stage implements StageSig { + import Param - private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) } + /* Begin: Stage logic. */ + bindingset[result, apa] + private ApApprox unbindApa(ApApprox apa) { + pragma[only_bind_out](apa) = pragma[only_bind_out](result) + } - bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, + Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, + pragma[only_bind_into](config)) and + matchesCall(ccc, call) + } - pragma[inline] - private Content getHeadContent(Ap ap) { exists(result) and ap = true } + /** + * Holds if `node` is reachable with access path `ap` from a source in the + * configuration `config`. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. + */ + pragma[nomagic] + predicate fwdFlow( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + fwdFlow0(node, state, cc, argAp, ap, config) and + PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and + filter(node, state, ap, config) + } - class ApOption = BooleanOption; + pragma[nomagic] + private predicate fwdFlow0( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + sourceNode(node, state, config) and + (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and + argAp = apNone() and + ap = getApNil(node) + or + exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | + fwdFlow(mid, state0, cc, argAp, ap0, config) and + localCc = getLocalCc(mid, cc) + | + localStep(mid, state0, node, state, true, _, config, localCc) and + ap = ap0 + or + localStep(mid, state0, node, state, false, ap, config, localCc) and + ap0 instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + jumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStateStep(mid, state0, node, state, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + // store + exists(TypedContent tc, Ap ap0 | + fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + ap = apCons(tc, ap0) + ) + or + // read + exists(Ap ap0, Content c | + fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowConsCand(ap0, c, ap, config) + ) + or + // flow into a callable + exists(ApApprox apa | + fwdFlowIn(_, node, state, _, cc, _, ap, config) and + apa = getApprox(ap) and + if PrevStage::parameterMayFlowThrough(node, _, apa, config) + then argAp = apSome(ap) + else argAp = apNone() + ) + or + // flow out of a callable + fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) + ) + } - ApOption apNone() { result = TBooleanNone() } + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + exists(DataFlowType contentType | + fwdFlow(node1, state, cc, argAp, ap1, config) and + PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and + typecheckStore(ap1, contentType) + ) + } - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + /** + * Holds if forward flow with access path `tail` reaches a store of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(TypedContent tc | + fwdFlowStore(_, tail, tc, _, _, _, _, config) and + tc.getContent() = c and + cons = apCons(tc, tail) + ) + } + pragma[nomagic] + private predicate fwdFlowRead( + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + fwdFlow(node1, state, cc, argAp, ap, config) and + PrevStage::readStepCand(node1, c, node2, config) and + getHeadContent(ap) = c + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, + Ap ap, Configuration config + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, state, outercc, argAp, ap, config) and + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutNotFromArg( + NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + ) { + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, argAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + ccOut = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + ) + } + + pragma[nomagic] + private predicate storeStepFwd( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config + ) { + fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + ap2 = apCons(tc, ap1) and + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + } + + private predicate readStepFwd( + NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config + ) { + fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowConsCand(ap1, c, ap2, config) + } + + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and + fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), + pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + + pragma[nomagic] + private predicate returnNodeMayFlowThrough( + RetNodeEx ret, FlowState state, Ap ap, Configuration config + ) { + fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from the + * enclosing callable in order to reach a sink, and if so, `returnAp` records + * the access path of the returned value. + */ + pragma[nomagic] + predicate revFlow( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + revFlow0(node, state, toReturn, returnAp, ap, config) and + fwdFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + fwdFlow(node, state, _, _, ap, config) and + sinkNode(node, state, config) and + (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid, FlowState state0 | + localStep(node, state, mid, state0, true, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, ap, config) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, state, _, _, ap, config) and + toReturn = false and + returnAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStep(node, mid, config) and + revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStateStep(node, state, mid, state0, config) and + revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, + pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowConsCand(ap0, c, ap, config) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, state, toReturn, returnAp, ap0, config) and + readStepFwd(node, ap, _, mid, ap0, config) + ) + or + // flow into a callable + revFlowInNotToReturn(node, state, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + or + // flow out of a callable + revFlowOut(_, node, state, _, _, ap, config) and + toReturn = true and + if returnNodeMayFlowThrough(node, state, ap, config) + then returnAp = apSome(ap) + else returnAp = apNone() + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, + boolean toReturn, ApOption returnAp, Configuration config + ) { + revFlow(mid, state, toReturn, returnAp, ap0, config) and + storeStepFwd(node, ap, tc, mid, ap0, config) and + tc.getContent() = c + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, _, tail, config) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0, config) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, + Configuration config + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, state, toReturn, returnAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInNotToReturn( + ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, FlowState state, CcCall ccc | + revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and + fwdFlow(ret, state, ccc, apSome(_), ap, config) and + matchesCall(ccc, call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Ap ap2, Content c | + PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and + revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and + revFlowConsCand(ap2, c, ap1, config) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and + readStepFwd(node1, ap1, c, node2, ap2, config) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, + pragma[only_bind_into](config)) + ) + } + + predicate revFlow(NodeEx node, FlowState state, Configuration config) { + revFlow(node, state, _, _, _, config) + } + + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, ap, config) + } + + private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepFwd(_, ap, tc, _, _, config) + } + + private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepCand(_, ap, tc, _, _, config) + } + + private predicate validAp(Ap ap, Configuration config) { + revFlow(_, _, _, _, ap, config) and ap instanceof ApNil + or + exists(TypedContent head, Ap tail | + consCand(head, tail, config) and + ap = apCons(head, tail) + ) + } + + predicate consCand(TypedContent tc, Ap ap, Configuration config) { + revConsCand(tc, ap, config) and + validAp(ap, config) + } + + pragma[noinline] + private predicate parameterFlow( + ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ) { + revFlow(p, _, true, apSome(ap0), ap, config) and + c = p.getEnclosingCallable() + } + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | + parameterFlow(p, ap, ap0, c, config) and + c = ret.getEnclosingCallable() and + revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), + pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and + fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and + kind = ret.getKind() and + p.getPosition() = pos and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists( + Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + | + revFlow(arg, state, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + + predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, argAp, ap, config) + ) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | consCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and + states = count(FlowState state | revFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | + revFlow(n, state, b, retAp, ap, config) + ) + } + /* End: Stage logic. */ + } +} + +private module BooleanCallContext { + class Cc extends boolean { + Cc() { this in [true, false] } + } + + class CcCall extends Cc { + CcCall() { this = true } + } + + /** Holds if the call context may be `call`. */ + predicate matchesCall(CcCall cc, DataFlowCall call) { any() } + + class CcNoCall extends Cc { + CcNoCall() { this = false } + } + + Cc ccNone() { result = false } + + CcCall ccSomeCall() { result = true } + + class LocalCc = Unit; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } +} + +private module Level1CallContext { class Cc = CallContext; class CcCall = CallContextCall; + pragma[inline] + predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } + class CcNoCall = CallContextNoCall; Cc ccNone() { result instanceof CallContextAny } CcCall ccSomeCall() { result instanceof CallContextSomeCall } - private class LocalCc = Unit; + module NoLocalCallContext { + class LocalCc = Unit; - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSiteDispatch(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSiteDispatch(call, c) + then result = TSpecificCall(call) + else result = TSomeCall() + } + } + + module LocalCallContext { + class LocalCc = LocalCallContext; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { + result = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + node.getEnclosingCallable()) + } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() + } } bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { checkCallContextReturn(innercc, c, call) and if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() } +} - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } +private module Stage2Param implements MkStage::StageParam { + private module PrevStage = Stage1; + + class Ap extends boolean { + Ap() { this in [true, false] } + } + + class ApNil extends Ap { + ApNil() { this = false } + } + + bindingset[result, ap] + PrevStage::Ap getApprox(Ap ap) { any() } + + ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + + pragma[inline] + Content getHeadContent(Ap ap) { exists(result) and ap = true } + + class ApOption = BooleanOption; + + ApOption apNone() { result = TBooleanNone() } + + ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + + import Level1CallContext + import NoLocalCallContext bindingset[node1, state1, config] bindingset[node2, state2, config] - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -1221,9 +1906,9 @@ private module Stage2 { exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/5; - private predicate flowIntoCall = flowIntoCallNodeCand1/5; + predicate flowIntoCall = flowIntoCallNodeCand1/5; pragma[nomagic] private predicate expectsContentCand(NodeEx node, Configuration config) { @@ -1235,7 +1920,7 @@ private module Stage2 { } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and not stateBarrier(node, state, config) and @@ -1248,544 +1933,11 @@ private module Stage2 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 2 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 2 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage2 = MkStage::Stage; + pragma[nomagic] private predicate flowOutOfCallNodeCand2( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config @@ -1883,14 +2035,13 @@ private module LocalFlowBigStep { ) { additionalLocalFlowStepNodeCand1(node1, node2, config) and state1 = state2 and - Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false, - pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false, + Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false, pragma[only_bind_into](config)) or additionalLocalStateStep(node1, state1, node2, state2, config) and - Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config)) + Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config)) } /** @@ -1967,26 +2118,24 @@ private module LocalFlowBigStep { private import LocalFlowBigStep -private module Stage3 { - module PrevStage = Stage2; - - class ApApprox = PrevStage::Ap; +private module Stage3Param implements MkStage::StageParam { + private module PrevStage = Stage2; class Ap = AccessPathFront; class ApNil = AccessPathFrontNil; - private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } + PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } + Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathFrontOption; @@ -1994,44 +2143,18 @@ private module Stage3 { ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - class Cc = boolean; + import BooleanCallContext - class CcCall extends Cc { - CcCall() { this = true } - - /** Holds if this call context may be `call`. */ - predicate matchesCall(DataFlowCall call) { any() } - } - - class CcNoCall extends Cc { - CcNoCall() { this = false } - } - - Cc ccNone() { result = false } - - CcCall ccSomeCall() { result = true } - - private class LocalCc = Unit; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/5; - private predicate flowIntoCall = flowIntoCallNodeCand2/5; + predicate flowIntoCall = flowIntoCallNodeCand2/5; pragma[nomagic] private predicate clearSet(NodeEx node, ContentSet c, Configuration config) { @@ -2067,7 +2190,7 @@ private module Stage3 { private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { exists(state) and exists(config) and not clear(node, ap, config) and @@ -2080,548 +2203,15 @@ private module Stage3 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { + predicate typecheckStore(Ap ap, DataFlowType contentType) { // We need to typecheck stores here, since reverse flow through a getter // might have a different type here compared to inside the getter. compatibleTypes(ap.getType(), contentType) } - - /* Begin: Stage 3 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 3 logic. */ } +private module Stage3 = MkStage::Stage; + /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` * and remains relevant for the following pruning stage. @@ -2644,7 +2234,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and nodes = strictcount(NodeEx n, FlowState state | - Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) or flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) ) and @@ -2828,26 +2418,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } } -private module Stage4 { - module PrevStage = Stage3; - - class ApApprox = PrevStage::Ap; +private module Stage4Param implements MkStage::StageParam { + private module PrevStage = Stage3; class Ap = AccessPathApprox; class ApNil = AccessPathApproxNil; - private ApApprox getApprox(Ap ap) { result = ap.getFront() } + PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } + Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathApproxOption; @@ -2855,38 +2443,10 @@ private module Stage4 { ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - class Cc = CallContext; + import Level1CallContext + import LocalCallContext - class CcCall = CallContextCall; - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - private class LocalCc = LocalCallContext; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() - } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { - result = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - node.getEnclosingCallable()) - } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -2894,575 +2454,40 @@ private module Stage4 { } pragma[nomagic] - private predicate flowOutOfCall( + predicate flowOutOfCall( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } pragma[nomagic] - private predicate flowIntoCall( + predicate flowIntoCall( DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } // Type checking is not necessary here as it has already been done in stage 3. bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 4 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 4 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage4 = MkStage::Stage; + bindingset[conf, result] private Configuration unbindConf(Configuration conf) { exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) @@ -3495,7 +2520,7 @@ private newtype TSummaryCtx = TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and - Stage4::revFlow(p, state, _, _, _, config) + Stage4::revFlow(p, state, _, config) ) } @@ -3553,7 +2578,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { result = strictcount(NodeEx n, FlowState state | - Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config) + Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config) ) } @@ -3667,7 +2692,7 @@ private newtype TPathNode = exists(PathNodeMid mid | pathStep(mid, node, state, cc, sc, ap) and pragma[only_bind_into](config) = mid.getConfiguration() and - Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config)) + Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config)) ) } or TPathNodeSink(NodeEx node, FlowState state, Configuration config) { @@ -4207,7 +3232,7 @@ private NodeEx getAnOutNodeFlow( ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result.asNode() = kind.getAnOutNode(call) and - Stage4::revFlow(result, _, _, _, apa, config) + Stage4::revFlow(result, _, apa, config) } /** @@ -4243,7 +3268,7 @@ private predicate parameterCand( DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config ) { exists(ParamNodeEx p | - Stage4::revFlow(p, _, _, _, apa, config) and + Stage4::revFlow(p, _, apa, config) and p.isParameterOf(callable, pos) ) } diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll index a076f7a2e45..22c3bd2466e 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,12 +944,9 @@ private module Stage1 { predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) } bindingset[node, state, config] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, toReturn, pragma[only_bind_into](config)) and + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, _, pragma[only_bind_into](config)) and exists(state) and - exists(returnAp) and exists(ap) } @@ -1142,66 +1139,754 @@ private predicate flowIntoCallNodeCand1( ) } -private module Stage2 { - module PrevStage = Stage1; +private signature module StageSig { + class Ap; + predicate revFlow(NodeEx node, Configuration config); + + bindingset[node, state, config] + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config); + + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ); + + predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config); +} + +private module MkStage { class ApApprox = PrevStage::Ap; - class Ap = boolean; + signature module StageParam { + class Ap; - class ApNil extends Ap { - ApNil() { this = false } + class ApNil extends Ap; + + bindingset[result, ap] + ApApprox getApprox(Ap ap); + + ApNil getApNil(NodeEx node); + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail); + + Content getHeadContent(Ap ap); + + class ApOption; + + ApOption apNone(); + + ApOption apSome(Ap ap); + + class Cc; + + class CcCall extends Cc; + + // TODO: member predicate on CcCall + predicate matchesCall(CcCall cc, DataFlowCall call); + + class CcNoCall extends Cc; + + Cc ccNone(); + + CcCall ccSomeCall(); + + class LocalCc; + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc); + + bindingset[node1, state1, config] + bindingset[node2, state2, config] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ); + + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + ); + + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ); + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config); + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType); } - bindingset[result, ap] - private ApApprox getApprox(Ap ap) { any() } + module Stage implements StageSig { + import Param - private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) } + /* Begin: Stage logic. */ + bindingset[result, apa] + private ApApprox unbindApa(ApApprox apa) { + pragma[only_bind_out](apa) = pragma[only_bind_out](result) + } - bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, + Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, + pragma[only_bind_into](config)) and + matchesCall(ccc, call) + } - pragma[inline] - private Content getHeadContent(Ap ap) { exists(result) and ap = true } + /** + * Holds if `node` is reachable with access path `ap` from a source in the + * configuration `config`. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. + */ + pragma[nomagic] + predicate fwdFlow( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + fwdFlow0(node, state, cc, argAp, ap, config) and + PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and + filter(node, state, ap, config) + } - class ApOption = BooleanOption; + pragma[nomagic] + private predicate fwdFlow0( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + sourceNode(node, state, config) and + (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and + argAp = apNone() and + ap = getApNil(node) + or + exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | + fwdFlow(mid, state0, cc, argAp, ap0, config) and + localCc = getLocalCc(mid, cc) + | + localStep(mid, state0, node, state, true, _, config, localCc) and + ap = ap0 + or + localStep(mid, state0, node, state, false, ap, config, localCc) and + ap0 instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + jumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStateStep(mid, state0, node, state, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + // store + exists(TypedContent tc, Ap ap0 | + fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + ap = apCons(tc, ap0) + ) + or + // read + exists(Ap ap0, Content c | + fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowConsCand(ap0, c, ap, config) + ) + or + // flow into a callable + exists(ApApprox apa | + fwdFlowIn(_, node, state, _, cc, _, ap, config) and + apa = getApprox(ap) and + if PrevStage::parameterMayFlowThrough(node, _, apa, config) + then argAp = apSome(ap) + else argAp = apNone() + ) + or + // flow out of a callable + fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) + ) + } - ApOption apNone() { result = TBooleanNone() } + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + exists(DataFlowType contentType | + fwdFlow(node1, state, cc, argAp, ap1, config) and + PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and + typecheckStore(ap1, contentType) + ) + } - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + /** + * Holds if forward flow with access path `tail` reaches a store of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(TypedContent tc | + fwdFlowStore(_, tail, tc, _, _, _, _, config) and + tc.getContent() = c and + cons = apCons(tc, tail) + ) + } + pragma[nomagic] + private predicate fwdFlowRead( + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + fwdFlow(node1, state, cc, argAp, ap, config) and + PrevStage::readStepCand(node1, c, node2, config) and + getHeadContent(ap) = c + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, + Ap ap, Configuration config + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, state, outercc, argAp, ap, config) and + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutNotFromArg( + NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + ) { + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, argAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + ccOut = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + ) + } + + pragma[nomagic] + private predicate storeStepFwd( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config + ) { + fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + ap2 = apCons(tc, ap1) and + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + } + + private predicate readStepFwd( + NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config + ) { + fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowConsCand(ap1, c, ap2, config) + } + + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and + fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), + pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + + pragma[nomagic] + private predicate returnNodeMayFlowThrough( + RetNodeEx ret, FlowState state, Ap ap, Configuration config + ) { + fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from the + * enclosing callable in order to reach a sink, and if so, `returnAp` records + * the access path of the returned value. + */ + pragma[nomagic] + predicate revFlow( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + revFlow0(node, state, toReturn, returnAp, ap, config) and + fwdFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + fwdFlow(node, state, _, _, ap, config) and + sinkNode(node, state, config) and + (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid, FlowState state0 | + localStep(node, state, mid, state0, true, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, ap, config) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, state, _, _, ap, config) and + toReturn = false and + returnAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStep(node, mid, config) and + revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStateStep(node, state, mid, state0, config) and + revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, + pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowConsCand(ap0, c, ap, config) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, state, toReturn, returnAp, ap0, config) and + readStepFwd(node, ap, _, mid, ap0, config) + ) + or + // flow into a callable + revFlowInNotToReturn(node, state, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + or + // flow out of a callable + revFlowOut(_, node, state, _, _, ap, config) and + toReturn = true and + if returnNodeMayFlowThrough(node, state, ap, config) + then returnAp = apSome(ap) + else returnAp = apNone() + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, + boolean toReturn, ApOption returnAp, Configuration config + ) { + revFlow(mid, state, toReturn, returnAp, ap0, config) and + storeStepFwd(node, ap, tc, mid, ap0, config) and + tc.getContent() = c + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, _, tail, config) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0, config) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, + Configuration config + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, state, toReturn, returnAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInNotToReturn( + ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, FlowState state, CcCall ccc | + revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and + fwdFlow(ret, state, ccc, apSome(_), ap, config) and + matchesCall(ccc, call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Ap ap2, Content c | + PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and + revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and + revFlowConsCand(ap2, c, ap1, config) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and + readStepFwd(node1, ap1, c, node2, ap2, config) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, + pragma[only_bind_into](config)) + ) + } + + predicate revFlow(NodeEx node, FlowState state, Configuration config) { + revFlow(node, state, _, _, _, config) + } + + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, ap, config) + } + + private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepFwd(_, ap, tc, _, _, config) + } + + private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepCand(_, ap, tc, _, _, config) + } + + private predicate validAp(Ap ap, Configuration config) { + revFlow(_, _, _, _, ap, config) and ap instanceof ApNil + or + exists(TypedContent head, Ap tail | + consCand(head, tail, config) and + ap = apCons(head, tail) + ) + } + + predicate consCand(TypedContent tc, Ap ap, Configuration config) { + revConsCand(tc, ap, config) and + validAp(ap, config) + } + + pragma[noinline] + private predicate parameterFlow( + ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ) { + revFlow(p, _, true, apSome(ap0), ap, config) and + c = p.getEnclosingCallable() + } + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | + parameterFlow(p, ap, ap0, c, config) and + c = ret.getEnclosingCallable() and + revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), + pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and + fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and + kind = ret.getKind() and + p.getPosition() = pos and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists( + Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + | + revFlow(arg, state, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + + predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, argAp, ap, config) + ) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | consCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and + states = count(FlowState state | revFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | + revFlow(n, state, b, retAp, ap, config) + ) + } + /* End: Stage logic. */ + } +} + +private module BooleanCallContext { + class Cc extends boolean { + Cc() { this in [true, false] } + } + + class CcCall extends Cc { + CcCall() { this = true } + } + + /** Holds if the call context may be `call`. */ + predicate matchesCall(CcCall cc, DataFlowCall call) { any() } + + class CcNoCall extends Cc { + CcNoCall() { this = false } + } + + Cc ccNone() { result = false } + + CcCall ccSomeCall() { result = true } + + class LocalCc = Unit; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } +} + +private module Level1CallContext { class Cc = CallContext; class CcCall = CallContextCall; + pragma[inline] + predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } + class CcNoCall = CallContextNoCall; Cc ccNone() { result instanceof CallContextAny } CcCall ccSomeCall() { result instanceof CallContextSomeCall } - private class LocalCc = Unit; + module NoLocalCallContext { + class LocalCc = Unit; - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSiteDispatch(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSiteDispatch(call, c) + then result = TSpecificCall(call) + else result = TSomeCall() + } + } + + module LocalCallContext { + class LocalCc = LocalCallContext; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { + result = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + node.getEnclosingCallable()) + } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() + } } bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { checkCallContextReturn(innercc, c, call) and if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() } +} - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } +private module Stage2Param implements MkStage::StageParam { + private module PrevStage = Stage1; + + class Ap extends boolean { + Ap() { this in [true, false] } + } + + class ApNil extends Ap { + ApNil() { this = false } + } + + bindingset[result, ap] + PrevStage::Ap getApprox(Ap ap) { any() } + + ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + + pragma[inline] + Content getHeadContent(Ap ap) { exists(result) and ap = true } + + class ApOption = BooleanOption; + + ApOption apNone() { result = TBooleanNone() } + + ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + + import Level1CallContext + import NoLocalCallContext bindingset[node1, state1, config] bindingset[node2, state2, config] - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -1221,9 +1906,9 @@ private module Stage2 { exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/5; - private predicate flowIntoCall = flowIntoCallNodeCand1/5; + predicate flowIntoCall = flowIntoCallNodeCand1/5; pragma[nomagic] private predicate expectsContentCand(NodeEx node, Configuration config) { @@ -1235,7 +1920,7 @@ private module Stage2 { } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and not stateBarrier(node, state, config) and @@ -1248,544 +1933,11 @@ private module Stage2 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 2 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 2 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage2 = MkStage::Stage; + pragma[nomagic] private predicate flowOutOfCallNodeCand2( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config @@ -1883,14 +2035,13 @@ private module LocalFlowBigStep { ) { additionalLocalFlowStepNodeCand1(node1, node2, config) and state1 = state2 and - Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false, - pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false, + Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false, pragma[only_bind_into](config)) or additionalLocalStateStep(node1, state1, node2, state2, config) and - Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config)) + Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config)) } /** @@ -1967,26 +2118,24 @@ private module LocalFlowBigStep { private import LocalFlowBigStep -private module Stage3 { - module PrevStage = Stage2; - - class ApApprox = PrevStage::Ap; +private module Stage3Param implements MkStage::StageParam { + private module PrevStage = Stage2; class Ap = AccessPathFront; class ApNil = AccessPathFrontNil; - private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } + PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } + Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathFrontOption; @@ -1994,44 +2143,18 @@ private module Stage3 { ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - class Cc = boolean; + import BooleanCallContext - class CcCall extends Cc { - CcCall() { this = true } - - /** Holds if this call context may be `call`. */ - predicate matchesCall(DataFlowCall call) { any() } - } - - class CcNoCall extends Cc { - CcNoCall() { this = false } - } - - Cc ccNone() { result = false } - - CcCall ccSomeCall() { result = true } - - private class LocalCc = Unit; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/5; - private predicate flowIntoCall = flowIntoCallNodeCand2/5; + predicate flowIntoCall = flowIntoCallNodeCand2/5; pragma[nomagic] private predicate clearSet(NodeEx node, ContentSet c, Configuration config) { @@ -2067,7 +2190,7 @@ private module Stage3 { private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { exists(state) and exists(config) and not clear(node, ap, config) and @@ -2080,548 +2203,15 @@ private module Stage3 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { + predicate typecheckStore(Ap ap, DataFlowType contentType) { // We need to typecheck stores here, since reverse flow through a getter // might have a different type here compared to inside the getter. compatibleTypes(ap.getType(), contentType) } - - /* Begin: Stage 3 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 3 logic. */ } +private module Stage3 = MkStage::Stage; + /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` * and remains relevant for the following pruning stage. @@ -2644,7 +2234,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and nodes = strictcount(NodeEx n, FlowState state | - Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) or flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) ) and @@ -2828,26 +2418,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } } -private module Stage4 { - module PrevStage = Stage3; - - class ApApprox = PrevStage::Ap; +private module Stage4Param implements MkStage::StageParam { + private module PrevStage = Stage3; class Ap = AccessPathApprox; class ApNil = AccessPathApproxNil; - private ApApprox getApprox(Ap ap) { result = ap.getFront() } + PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } + Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathApproxOption; @@ -2855,38 +2443,10 @@ private module Stage4 { ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - class Cc = CallContext; + import Level1CallContext + import LocalCallContext - class CcCall = CallContextCall; - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - private class LocalCc = LocalCallContext; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() - } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { - result = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - node.getEnclosingCallable()) - } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -2894,575 +2454,40 @@ private module Stage4 { } pragma[nomagic] - private predicate flowOutOfCall( + predicate flowOutOfCall( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } pragma[nomagic] - private predicate flowIntoCall( + predicate flowIntoCall( DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } // Type checking is not necessary here as it has already been done in stage 3. bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 4 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 4 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage4 = MkStage::Stage; + bindingset[conf, result] private Configuration unbindConf(Configuration conf) { exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) @@ -3495,7 +2520,7 @@ private newtype TSummaryCtx = TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and - Stage4::revFlow(p, state, _, _, _, config) + Stage4::revFlow(p, state, _, config) ) } @@ -3553,7 +2578,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { result = strictcount(NodeEx n, FlowState state | - Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config) + Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config) ) } @@ -3667,7 +2692,7 @@ private newtype TPathNode = exists(PathNodeMid mid | pathStep(mid, node, state, cc, sc, ap) and pragma[only_bind_into](config) = mid.getConfiguration() and - Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config)) + Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config)) ) } or TPathNodeSink(NodeEx node, FlowState state, Configuration config) { @@ -4207,7 +3232,7 @@ private NodeEx getAnOutNodeFlow( ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result.asNode() = kind.getAnOutNode(call) and - Stage4::revFlow(result, _, _, _, apa, config) + Stage4::revFlow(result, _, apa, config) } /** @@ -4243,7 +3268,7 @@ private predicate parameterCand( DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config ) { exists(ParamNodeEx p | - Stage4::revFlow(p, _, _, _, apa, config) and + Stage4::revFlow(p, _, apa, config) and p.isParameterOf(callable, pos) ) } diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll index a076f7a2e45..22c3bd2466e 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,12 +944,9 @@ private module Stage1 { predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) } bindingset[node, state, config] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, toReturn, pragma[only_bind_into](config)) and + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, _, pragma[only_bind_into](config)) and exists(state) and - exists(returnAp) and exists(ap) } @@ -1142,66 +1139,754 @@ private predicate flowIntoCallNodeCand1( ) } -private module Stage2 { - module PrevStage = Stage1; +private signature module StageSig { + class Ap; + predicate revFlow(NodeEx node, Configuration config); + + bindingset[node, state, config] + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config); + + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ); + + predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config); +} + +private module MkStage { class ApApprox = PrevStage::Ap; - class Ap = boolean; + signature module StageParam { + class Ap; - class ApNil extends Ap { - ApNil() { this = false } + class ApNil extends Ap; + + bindingset[result, ap] + ApApprox getApprox(Ap ap); + + ApNil getApNil(NodeEx node); + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail); + + Content getHeadContent(Ap ap); + + class ApOption; + + ApOption apNone(); + + ApOption apSome(Ap ap); + + class Cc; + + class CcCall extends Cc; + + // TODO: member predicate on CcCall + predicate matchesCall(CcCall cc, DataFlowCall call); + + class CcNoCall extends Cc; + + Cc ccNone(); + + CcCall ccSomeCall(); + + class LocalCc; + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc); + + bindingset[node1, state1, config] + bindingset[node2, state2, config] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ); + + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + ); + + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ); + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config); + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType); } - bindingset[result, ap] - private ApApprox getApprox(Ap ap) { any() } + module Stage implements StageSig { + import Param - private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) } + /* Begin: Stage logic. */ + bindingset[result, apa] + private ApApprox unbindApa(ApApprox apa) { + pragma[only_bind_out](apa) = pragma[only_bind_out](result) + } - bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, + Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, + pragma[only_bind_into](config)) and + matchesCall(ccc, call) + } - pragma[inline] - private Content getHeadContent(Ap ap) { exists(result) and ap = true } + /** + * Holds if `node` is reachable with access path `ap` from a source in the + * configuration `config`. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. + */ + pragma[nomagic] + predicate fwdFlow( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + fwdFlow0(node, state, cc, argAp, ap, config) and + PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and + filter(node, state, ap, config) + } - class ApOption = BooleanOption; + pragma[nomagic] + private predicate fwdFlow0( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + sourceNode(node, state, config) and + (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and + argAp = apNone() and + ap = getApNil(node) + or + exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | + fwdFlow(mid, state0, cc, argAp, ap0, config) and + localCc = getLocalCc(mid, cc) + | + localStep(mid, state0, node, state, true, _, config, localCc) and + ap = ap0 + or + localStep(mid, state0, node, state, false, ap, config, localCc) and + ap0 instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + jumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStateStep(mid, state0, node, state, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + // store + exists(TypedContent tc, Ap ap0 | + fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + ap = apCons(tc, ap0) + ) + or + // read + exists(Ap ap0, Content c | + fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowConsCand(ap0, c, ap, config) + ) + or + // flow into a callable + exists(ApApprox apa | + fwdFlowIn(_, node, state, _, cc, _, ap, config) and + apa = getApprox(ap) and + if PrevStage::parameterMayFlowThrough(node, _, apa, config) + then argAp = apSome(ap) + else argAp = apNone() + ) + or + // flow out of a callable + fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) + ) + } - ApOption apNone() { result = TBooleanNone() } + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + exists(DataFlowType contentType | + fwdFlow(node1, state, cc, argAp, ap1, config) and + PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and + typecheckStore(ap1, contentType) + ) + } - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + /** + * Holds if forward flow with access path `tail` reaches a store of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(TypedContent tc | + fwdFlowStore(_, tail, tc, _, _, _, _, config) and + tc.getContent() = c and + cons = apCons(tc, tail) + ) + } + pragma[nomagic] + private predicate fwdFlowRead( + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + fwdFlow(node1, state, cc, argAp, ap, config) and + PrevStage::readStepCand(node1, c, node2, config) and + getHeadContent(ap) = c + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, + Ap ap, Configuration config + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, state, outercc, argAp, ap, config) and + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutNotFromArg( + NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + ) { + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, argAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + ccOut = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + ) + } + + pragma[nomagic] + private predicate storeStepFwd( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config + ) { + fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + ap2 = apCons(tc, ap1) and + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + } + + private predicate readStepFwd( + NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config + ) { + fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowConsCand(ap1, c, ap2, config) + } + + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and + fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), + pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + + pragma[nomagic] + private predicate returnNodeMayFlowThrough( + RetNodeEx ret, FlowState state, Ap ap, Configuration config + ) { + fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from the + * enclosing callable in order to reach a sink, and if so, `returnAp` records + * the access path of the returned value. + */ + pragma[nomagic] + predicate revFlow( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + revFlow0(node, state, toReturn, returnAp, ap, config) and + fwdFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + fwdFlow(node, state, _, _, ap, config) and + sinkNode(node, state, config) and + (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid, FlowState state0 | + localStep(node, state, mid, state0, true, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, ap, config) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, state, _, _, ap, config) and + toReturn = false and + returnAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStep(node, mid, config) and + revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStateStep(node, state, mid, state0, config) and + revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, + pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowConsCand(ap0, c, ap, config) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, state, toReturn, returnAp, ap0, config) and + readStepFwd(node, ap, _, mid, ap0, config) + ) + or + // flow into a callable + revFlowInNotToReturn(node, state, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + or + // flow out of a callable + revFlowOut(_, node, state, _, _, ap, config) and + toReturn = true and + if returnNodeMayFlowThrough(node, state, ap, config) + then returnAp = apSome(ap) + else returnAp = apNone() + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, + boolean toReturn, ApOption returnAp, Configuration config + ) { + revFlow(mid, state, toReturn, returnAp, ap0, config) and + storeStepFwd(node, ap, tc, mid, ap0, config) and + tc.getContent() = c + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, _, tail, config) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0, config) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, + Configuration config + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, state, toReturn, returnAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInNotToReturn( + ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, FlowState state, CcCall ccc | + revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and + fwdFlow(ret, state, ccc, apSome(_), ap, config) and + matchesCall(ccc, call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Ap ap2, Content c | + PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and + revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and + revFlowConsCand(ap2, c, ap1, config) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and + readStepFwd(node1, ap1, c, node2, ap2, config) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, + pragma[only_bind_into](config)) + ) + } + + predicate revFlow(NodeEx node, FlowState state, Configuration config) { + revFlow(node, state, _, _, _, config) + } + + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, ap, config) + } + + private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepFwd(_, ap, tc, _, _, config) + } + + private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepCand(_, ap, tc, _, _, config) + } + + private predicate validAp(Ap ap, Configuration config) { + revFlow(_, _, _, _, ap, config) and ap instanceof ApNil + or + exists(TypedContent head, Ap tail | + consCand(head, tail, config) and + ap = apCons(head, tail) + ) + } + + predicate consCand(TypedContent tc, Ap ap, Configuration config) { + revConsCand(tc, ap, config) and + validAp(ap, config) + } + + pragma[noinline] + private predicate parameterFlow( + ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ) { + revFlow(p, _, true, apSome(ap0), ap, config) and + c = p.getEnclosingCallable() + } + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | + parameterFlow(p, ap, ap0, c, config) and + c = ret.getEnclosingCallable() and + revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), + pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and + fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and + kind = ret.getKind() and + p.getPosition() = pos and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists( + Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + | + revFlow(arg, state, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + + predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, argAp, ap, config) + ) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | consCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and + states = count(FlowState state | revFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | + revFlow(n, state, b, retAp, ap, config) + ) + } + /* End: Stage logic. */ + } +} + +private module BooleanCallContext { + class Cc extends boolean { + Cc() { this in [true, false] } + } + + class CcCall extends Cc { + CcCall() { this = true } + } + + /** Holds if the call context may be `call`. */ + predicate matchesCall(CcCall cc, DataFlowCall call) { any() } + + class CcNoCall extends Cc { + CcNoCall() { this = false } + } + + Cc ccNone() { result = false } + + CcCall ccSomeCall() { result = true } + + class LocalCc = Unit; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } +} + +private module Level1CallContext { class Cc = CallContext; class CcCall = CallContextCall; + pragma[inline] + predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } + class CcNoCall = CallContextNoCall; Cc ccNone() { result instanceof CallContextAny } CcCall ccSomeCall() { result instanceof CallContextSomeCall } - private class LocalCc = Unit; + module NoLocalCallContext { + class LocalCc = Unit; - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSiteDispatch(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSiteDispatch(call, c) + then result = TSpecificCall(call) + else result = TSomeCall() + } + } + + module LocalCallContext { + class LocalCc = LocalCallContext; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { + result = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + node.getEnclosingCallable()) + } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() + } } bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { checkCallContextReturn(innercc, c, call) and if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() } +} - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } +private module Stage2Param implements MkStage::StageParam { + private module PrevStage = Stage1; + + class Ap extends boolean { + Ap() { this in [true, false] } + } + + class ApNil extends Ap { + ApNil() { this = false } + } + + bindingset[result, ap] + PrevStage::Ap getApprox(Ap ap) { any() } + + ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + + pragma[inline] + Content getHeadContent(Ap ap) { exists(result) and ap = true } + + class ApOption = BooleanOption; + + ApOption apNone() { result = TBooleanNone() } + + ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + + import Level1CallContext + import NoLocalCallContext bindingset[node1, state1, config] bindingset[node2, state2, config] - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -1221,9 +1906,9 @@ private module Stage2 { exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/5; - private predicate flowIntoCall = flowIntoCallNodeCand1/5; + predicate flowIntoCall = flowIntoCallNodeCand1/5; pragma[nomagic] private predicate expectsContentCand(NodeEx node, Configuration config) { @@ -1235,7 +1920,7 @@ private module Stage2 { } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and not stateBarrier(node, state, config) and @@ -1248,544 +1933,11 @@ private module Stage2 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 2 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 2 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage2 = MkStage::Stage; + pragma[nomagic] private predicate flowOutOfCallNodeCand2( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config @@ -1883,14 +2035,13 @@ private module LocalFlowBigStep { ) { additionalLocalFlowStepNodeCand1(node1, node2, config) and state1 = state2 and - Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false, - pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false, + Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false, pragma[only_bind_into](config)) or additionalLocalStateStep(node1, state1, node2, state2, config) and - Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config)) + Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config)) } /** @@ -1967,26 +2118,24 @@ private module LocalFlowBigStep { private import LocalFlowBigStep -private module Stage3 { - module PrevStage = Stage2; - - class ApApprox = PrevStage::Ap; +private module Stage3Param implements MkStage::StageParam { + private module PrevStage = Stage2; class Ap = AccessPathFront; class ApNil = AccessPathFrontNil; - private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } + PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } + Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathFrontOption; @@ -1994,44 +2143,18 @@ private module Stage3 { ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - class Cc = boolean; + import BooleanCallContext - class CcCall extends Cc { - CcCall() { this = true } - - /** Holds if this call context may be `call`. */ - predicate matchesCall(DataFlowCall call) { any() } - } - - class CcNoCall extends Cc { - CcNoCall() { this = false } - } - - Cc ccNone() { result = false } - - CcCall ccSomeCall() { result = true } - - private class LocalCc = Unit; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/5; - private predicate flowIntoCall = flowIntoCallNodeCand2/5; + predicate flowIntoCall = flowIntoCallNodeCand2/5; pragma[nomagic] private predicate clearSet(NodeEx node, ContentSet c, Configuration config) { @@ -2067,7 +2190,7 @@ private module Stage3 { private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { exists(state) and exists(config) and not clear(node, ap, config) and @@ -2080,548 +2203,15 @@ private module Stage3 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { + predicate typecheckStore(Ap ap, DataFlowType contentType) { // We need to typecheck stores here, since reverse flow through a getter // might have a different type here compared to inside the getter. compatibleTypes(ap.getType(), contentType) } - - /* Begin: Stage 3 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 3 logic. */ } +private module Stage3 = MkStage::Stage; + /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` * and remains relevant for the following pruning stage. @@ -2644,7 +2234,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and nodes = strictcount(NodeEx n, FlowState state | - Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) or flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) ) and @@ -2828,26 +2418,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } } -private module Stage4 { - module PrevStage = Stage3; - - class ApApprox = PrevStage::Ap; +private module Stage4Param implements MkStage::StageParam { + private module PrevStage = Stage3; class Ap = AccessPathApprox; class ApNil = AccessPathApproxNil; - private ApApprox getApprox(Ap ap) { result = ap.getFront() } + PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } + Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathApproxOption; @@ -2855,38 +2443,10 @@ private module Stage4 { ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - class Cc = CallContext; + import Level1CallContext + import LocalCallContext - class CcCall = CallContextCall; - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - private class LocalCc = LocalCallContext; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() - } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { - result = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - node.getEnclosingCallable()) - } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -2894,575 +2454,40 @@ private module Stage4 { } pragma[nomagic] - private predicate flowOutOfCall( + predicate flowOutOfCall( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } pragma[nomagic] - private predicate flowIntoCall( + predicate flowIntoCall( DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } // Type checking is not necessary here as it has already been done in stage 3. bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 4 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 4 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage4 = MkStage::Stage; + bindingset[conf, result] private Configuration unbindConf(Configuration conf) { exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) @@ -3495,7 +2520,7 @@ private newtype TSummaryCtx = TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and - Stage4::revFlow(p, state, _, _, _, config) + Stage4::revFlow(p, state, _, config) ) } @@ -3553,7 +2578,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { result = strictcount(NodeEx n, FlowState state | - Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config) + Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config) ) } @@ -3667,7 +2692,7 @@ private newtype TPathNode = exists(PathNodeMid mid | pathStep(mid, node, state, cc, sc, ap) and pragma[only_bind_into](config) = mid.getConfiguration() and - Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config)) + Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config)) ) } or TPathNodeSink(NodeEx node, FlowState state, Configuration config) { @@ -4207,7 +3232,7 @@ private NodeEx getAnOutNodeFlow( ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result.asNode() = kind.getAnOutNode(call) and - Stage4::revFlow(result, _, _, _, apa, config) + Stage4::revFlow(result, _, apa, config) } /** @@ -4243,7 +3268,7 @@ private predicate parameterCand( DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config ) { exists(ParamNodeEx p | - Stage4::revFlow(p, _, _, _, apa, config) and + Stage4::revFlow(p, _, apa, config) and p.isParameterOf(callable, pos) ) } diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForOnActivityResult.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForOnActivityResult.qll index a076f7a2e45..22c3bd2466e 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForOnActivityResult.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForOnActivityResult.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,12 +944,9 @@ private module Stage1 { predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) } bindingset[node, state, config] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, toReturn, pragma[only_bind_into](config)) and + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, _, pragma[only_bind_into](config)) and exists(state) and - exists(returnAp) and exists(ap) } @@ -1142,66 +1139,754 @@ private predicate flowIntoCallNodeCand1( ) } -private module Stage2 { - module PrevStage = Stage1; +private signature module StageSig { + class Ap; + predicate revFlow(NodeEx node, Configuration config); + + bindingset[node, state, config] + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config); + + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ); + + predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config); +} + +private module MkStage { class ApApprox = PrevStage::Ap; - class Ap = boolean; + signature module StageParam { + class Ap; - class ApNil extends Ap { - ApNil() { this = false } + class ApNil extends Ap; + + bindingset[result, ap] + ApApprox getApprox(Ap ap); + + ApNil getApNil(NodeEx node); + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail); + + Content getHeadContent(Ap ap); + + class ApOption; + + ApOption apNone(); + + ApOption apSome(Ap ap); + + class Cc; + + class CcCall extends Cc; + + // TODO: member predicate on CcCall + predicate matchesCall(CcCall cc, DataFlowCall call); + + class CcNoCall extends Cc; + + Cc ccNone(); + + CcCall ccSomeCall(); + + class LocalCc; + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc); + + bindingset[node1, state1, config] + bindingset[node2, state2, config] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ); + + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + ); + + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ); + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config); + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType); } - bindingset[result, ap] - private ApApprox getApprox(Ap ap) { any() } + module Stage implements StageSig { + import Param - private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) } + /* Begin: Stage logic. */ + bindingset[result, apa] + private ApApprox unbindApa(ApApprox apa) { + pragma[only_bind_out](apa) = pragma[only_bind_out](result) + } - bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, + Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, + pragma[only_bind_into](config)) and + matchesCall(ccc, call) + } - pragma[inline] - private Content getHeadContent(Ap ap) { exists(result) and ap = true } + /** + * Holds if `node` is reachable with access path `ap` from a source in the + * configuration `config`. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. + */ + pragma[nomagic] + predicate fwdFlow( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + fwdFlow0(node, state, cc, argAp, ap, config) and + PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and + filter(node, state, ap, config) + } - class ApOption = BooleanOption; + pragma[nomagic] + private predicate fwdFlow0( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + sourceNode(node, state, config) and + (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and + argAp = apNone() and + ap = getApNil(node) + or + exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | + fwdFlow(mid, state0, cc, argAp, ap0, config) and + localCc = getLocalCc(mid, cc) + | + localStep(mid, state0, node, state, true, _, config, localCc) and + ap = ap0 + or + localStep(mid, state0, node, state, false, ap, config, localCc) and + ap0 instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + jumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStateStep(mid, state0, node, state, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + // store + exists(TypedContent tc, Ap ap0 | + fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + ap = apCons(tc, ap0) + ) + or + // read + exists(Ap ap0, Content c | + fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowConsCand(ap0, c, ap, config) + ) + or + // flow into a callable + exists(ApApprox apa | + fwdFlowIn(_, node, state, _, cc, _, ap, config) and + apa = getApprox(ap) and + if PrevStage::parameterMayFlowThrough(node, _, apa, config) + then argAp = apSome(ap) + else argAp = apNone() + ) + or + // flow out of a callable + fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) + ) + } - ApOption apNone() { result = TBooleanNone() } + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + exists(DataFlowType contentType | + fwdFlow(node1, state, cc, argAp, ap1, config) and + PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and + typecheckStore(ap1, contentType) + ) + } - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + /** + * Holds if forward flow with access path `tail` reaches a store of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(TypedContent tc | + fwdFlowStore(_, tail, tc, _, _, _, _, config) and + tc.getContent() = c and + cons = apCons(tc, tail) + ) + } + pragma[nomagic] + private predicate fwdFlowRead( + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + fwdFlow(node1, state, cc, argAp, ap, config) and + PrevStage::readStepCand(node1, c, node2, config) and + getHeadContent(ap) = c + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, + Ap ap, Configuration config + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, state, outercc, argAp, ap, config) and + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutNotFromArg( + NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + ) { + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, argAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + ccOut = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + ) + } + + pragma[nomagic] + private predicate storeStepFwd( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config + ) { + fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + ap2 = apCons(tc, ap1) and + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + } + + private predicate readStepFwd( + NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config + ) { + fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowConsCand(ap1, c, ap2, config) + } + + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and + fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), + pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + + pragma[nomagic] + private predicate returnNodeMayFlowThrough( + RetNodeEx ret, FlowState state, Ap ap, Configuration config + ) { + fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from the + * enclosing callable in order to reach a sink, and if so, `returnAp` records + * the access path of the returned value. + */ + pragma[nomagic] + predicate revFlow( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + revFlow0(node, state, toReturn, returnAp, ap, config) and + fwdFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + fwdFlow(node, state, _, _, ap, config) and + sinkNode(node, state, config) and + (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid, FlowState state0 | + localStep(node, state, mid, state0, true, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, ap, config) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, state, _, _, ap, config) and + toReturn = false and + returnAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStep(node, mid, config) and + revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStateStep(node, state, mid, state0, config) and + revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, + pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowConsCand(ap0, c, ap, config) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, state, toReturn, returnAp, ap0, config) and + readStepFwd(node, ap, _, mid, ap0, config) + ) + or + // flow into a callable + revFlowInNotToReturn(node, state, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + or + // flow out of a callable + revFlowOut(_, node, state, _, _, ap, config) and + toReturn = true and + if returnNodeMayFlowThrough(node, state, ap, config) + then returnAp = apSome(ap) + else returnAp = apNone() + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, + boolean toReturn, ApOption returnAp, Configuration config + ) { + revFlow(mid, state, toReturn, returnAp, ap0, config) and + storeStepFwd(node, ap, tc, mid, ap0, config) and + tc.getContent() = c + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, _, tail, config) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0, config) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, + Configuration config + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, state, toReturn, returnAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInNotToReturn( + ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, FlowState state, CcCall ccc | + revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and + fwdFlow(ret, state, ccc, apSome(_), ap, config) and + matchesCall(ccc, call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Ap ap2, Content c | + PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and + revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and + revFlowConsCand(ap2, c, ap1, config) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and + readStepFwd(node1, ap1, c, node2, ap2, config) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, + pragma[only_bind_into](config)) + ) + } + + predicate revFlow(NodeEx node, FlowState state, Configuration config) { + revFlow(node, state, _, _, _, config) + } + + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, ap, config) + } + + private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepFwd(_, ap, tc, _, _, config) + } + + private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepCand(_, ap, tc, _, _, config) + } + + private predicate validAp(Ap ap, Configuration config) { + revFlow(_, _, _, _, ap, config) and ap instanceof ApNil + or + exists(TypedContent head, Ap tail | + consCand(head, tail, config) and + ap = apCons(head, tail) + ) + } + + predicate consCand(TypedContent tc, Ap ap, Configuration config) { + revConsCand(tc, ap, config) and + validAp(ap, config) + } + + pragma[noinline] + private predicate parameterFlow( + ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ) { + revFlow(p, _, true, apSome(ap0), ap, config) and + c = p.getEnclosingCallable() + } + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | + parameterFlow(p, ap, ap0, c, config) and + c = ret.getEnclosingCallable() and + revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), + pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and + fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and + kind = ret.getKind() and + p.getPosition() = pos and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists( + Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + | + revFlow(arg, state, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + + predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, argAp, ap, config) + ) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | consCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and + states = count(FlowState state | revFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | + revFlow(n, state, b, retAp, ap, config) + ) + } + /* End: Stage logic. */ + } +} + +private module BooleanCallContext { + class Cc extends boolean { + Cc() { this in [true, false] } + } + + class CcCall extends Cc { + CcCall() { this = true } + } + + /** Holds if the call context may be `call`. */ + predicate matchesCall(CcCall cc, DataFlowCall call) { any() } + + class CcNoCall extends Cc { + CcNoCall() { this = false } + } + + Cc ccNone() { result = false } + + CcCall ccSomeCall() { result = true } + + class LocalCc = Unit; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } +} + +private module Level1CallContext { class Cc = CallContext; class CcCall = CallContextCall; + pragma[inline] + predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } + class CcNoCall = CallContextNoCall; Cc ccNone() { result instanceof CallContextAny } CcCall ccSomeCall() { result instanceof CallContextSomeCall } - private class LocalCc = Unit; + module NoLocalCallContext { + class LocalCc = Unit; - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSiteDispatch(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSiteDispatch(call, c) + then result = TSpecificCall(call) + else result = TSomeCall() + } + } + + module LocalCallContext { + class LocalCc = LocalCallContext; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { + result = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + node.getEnclosingCallable()) + } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() + } } bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { checkCallContextReturn(innercc, c, call) and if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() } +} - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } +private module Stage2Param implements MkStage::StageParam { + private module PrevStage = Stage1; + + class Ap extends boolean { + Ap() { this in [true, false] } + } + + class ApNil extends Ap { + ApNil() { this = false } + } + + bindingset[result, ap] + PrevStage::Ap getApprox(Ap ap) { any() } + + ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + + pragma[inline] + Content getHeadContent(Ap ap) { exists(result) and ap = true } + + class ApOption = BooleanOption; + + ApOption apNone() { result = TBooleanNone() } + + ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + + import Level1CallContext + import NoLocalCallContext bindingset[node1, state1, config] bindingset[node2, state2, config] - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -1221,9 +1906,9 @@ private module Stage2 { exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/5; - private predicate flowIntoCall = flowIntoCallNodeCand1/5; + predicate flowIntoCall = flowIntoCallNodeCand1/5; pragma[nomagic] private predicate expectsContentCand(NodeEx node, Configuration config) { @@ -1235,7 +1920,7 @@ private module Stage2 { } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and not stateBarrier(node, state, config) and @@ -1248,544 +1933,11 @@ private module Stage2 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 2 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 2 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage2 = MkStage::Stage; + pragma[nomagic] private predicate flowOutOfCallNodeCand2( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config @@ -1883,14 +2035,13 @@ private module LocalFlowBigStep { ) { additionalLocalFlowStepNodeCand1(node1, node2, config) and state1 = state2 and - Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false, - pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false, + Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false, pragma[only_bind_into](config)) or additionalLocalStateStep(node1, state1, node2, state2, config) and - Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config)) + Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config)) } /** @@ -1967,26 +2118,24 @@ private module LocalFlowBigStep { private import LocalFlowBigStep -private module Stage3 { - module PrevStage = Stage2; - - class ApApprox = PrevStage::Ap; +private module Stage3Param implements MkStage::StageParam { + private module PrevStage = Stage2; class Ap = AccessPathFront; class ApNil = AccessPathFrontNil; - private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } + PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } + Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathFrontOption; @@ -1994,44 +2143,18 @@ private module Stage3 { ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - class Cc = boolean; + import BooleanCallContext - class CcCall extends Cc { - CcCall() { this = true } - - /** Holds if this call context may be `call`. */ - predicate matchesCall(DataFlowCall call) { any() } - } - - class CcNoCall extends Cc { - CcNoCall() { this = false } - } - - Cc ccNone() { result = false } - - CcCall ccSomeCall() { result = true } - - private class LocalCc = Unit; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/5; - private predicate flowIntoCall = flowIntoCallNodeCand2/5; + predicate flowIntoCall = flowIntoCallNodeCand2/5; pragma[nomagic] private predicate clearSet(NodeEx node, ContentSet c, Configuration config) { @@ -2067,7 +2190,7 @@ private module Stage3 { private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { exists(state) and exists(config) and not clear(node, ap, config) and @@ -2080,548 +2203,15 @@ private module Stage3 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { + predicate typecheckStore(Ap ap, DataFlowType contentType) { // We need to typecheck stores here, since reverse flow through a getter // might have a different type here compared to inside the getter. compatibleTypes(ap.getType(), contentType) } - - /* Begin: Stage 3 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 3 logic. */ } +private module Stage3 = MkStage::Stage; + /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` * and remains relevant for the following pruning stage. @@ -2644,7 +2234,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and nodes = strictcount(NodeEx n, FlowState state | - Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) or flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) ) and @@ -2828,26 +2418,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } } -private module Stage4 { - module PrevStage = Stage3; - - class ApApprox = PrevStage::Ap; +private module Stage4Param implements MkStage::StageParam { + private module PrevStage = Stage3; class Ap = AccessPathApprox; class ApNil = AccessPathApproxNil; - private ApApprox getApprox(Ap ap) { result = ap.getFront() } + PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } + Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathApproxOption; @@ -2855,38 +2443,10 @@ private module Stage4 { ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - class Cc = CallContext; + import Level1CallContext + import LocalCallContext - class CcCall = CallContextCall; - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - private class LocalCc = LocalCallContext; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() - } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { - result = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - node.getEnclosingCallable()) - } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -2894,575 +2454,40 @@ private module Stage4 { } pragma[nomagic] - private predicate flowOutOfCall( + predicate flowOutOfCall( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } pragma[nomagic] - private predicate flowIntoCall( + predicate flowIntoCall( DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } // Type checking is not necessary here as it has already been done in stage 3. bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 4 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 4 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage4 = MkStage::Stage; + bindingset[conf, result] private Configuration unbindConf(Configuration conf) { exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) @@ -3495,7 +2520,7 @@ private newtype TSummaryCtx = TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and - Stage4::revFlow(p, state, _, _, _, config) + Stage4::revFlow(p, state, _, config) ) } @@ -3553,7 +2578,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { result = strictcount(NodeEx n, FlowState state | - Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config) + Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config) ) } @@ -3667,7 +2692,7 @@ private newtype TPathNode = exists(PathNodeMid mid | pathStep(mid, node, state, cc, sc, ap) and pragma[only_bind_into](config) = mid.getConfiguration() and - Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config)) + Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config)) ) } or TPathNodeSink(NodeEx node, FlowState state, Configuration config) { @@ -4207,7 +3232,7 @@ private NodeEx getAnOutNodeFlow( ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result.asNode() = kind.getAnOutNode(call) and - Stage4::revFlow(result, _, _, _, apa, config) + Stage4::revFlow(result, _, apa, config) } /** @@ -4243,7 +3268,7 @@ private predicate parameterCand( DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config ) { exists(ParamNodeEx p | - Stage4::revFlow(p, _, _, _, apa, config) and + Stage4::revFlow(p, _, apa, config) and p.isParameterOf(callable, pos) ) } diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll index a076f7a2e45..22c3bd2466e 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,12 +944,9 @@ private module Stage1 { predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) } bindingset[node, state, config] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, toReturn, pragma[only_bind_into](config)) and + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, _, pragma[only_bind_into](config)) and exists(state) and - exists(returnAp) and exists(ap) } @@ -1142,66 +1139,754 @@ private predicate flowIntoCallNodeCand1( ) } -private module Stage2 { - module PrevStage = Stage1; +private signature module StageSig { + class Ap; + predicate revFlow(NodeEx node, Configuration config); + + bindingset[node, state, config] + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config); + + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ); + + predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config); +} + +private module MkStage { class ApApprox = PrevStage::Ap; - class Ap = boolean; + signature module StageParam { + class Ap; - class ApNil extends Ap { - ApNil() { this = false } + class ApNil extends Ap; + + bindingset[result, ap] + ApApprox getApprox(Ap ap); + + ApNil getApNil(NodeEx node); + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail); + + Content getHeadContent(Ap ap); + + class ApOption; + + ApOption apNone(); + + ApOption apSome(Ap ap); + + class Cc; + + class CcCall extends Cc; + + // TODO: member predicate on CcCall + predicate matchesCall(CcCall cc, DataFlowCall call); + + class CcNoCall extends Cc; + + Cc ccNone(); + + CcCall ccSomeCall(); + + class LocalCc; + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc); + + bindingset[node1, state1, config] + bindingset[node2, state2, config] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ); + + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + ); + + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ); + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config); + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType); } - bindingset[result, ap] - private ApApprox getApprox(Ap ap) { any() } + module Stage implements StageSig { + import Param - private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) } + /* Begin: Stage logic. */ + bindingset[result, apa] + private ApApprox unbindApa(ApApprox apa) { + pragma[only_bind_out](apa) = pragma[only_bind_out](result) + } - bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, + Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, + pragma[only_bind_into](config)) and + matchesCall(ccc, call) + } - pragma[inline] - private Content getHeadContent(Ap ap) { exists(result) and ap = true } + /** + * Holds if `node` is reachable with access path `ap` from a source in the + * configuration `config`. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. + */ + pragma[nomagic] + predicate fwdFlow( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + fwdFlow0(node, state, cc, argAp, ap, config) and + PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and + filter(node, state, ap, config) + } - class ApOption = BooleanOption; + pragma[nomagic] + private predicate fwdFlow0( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + sourceNode(node, state, config) and + (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and + argAp = apNone() and + ap = getApNil(node) + or + exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | + fwdFlow(mid, state0, cc, argAp, ap0, config) and + localCc = getLocalCc(mid, cc) + | + localStep(mid, state0, node, state, true, _, config, localCc) and + ap = ap0 + or + localStep(mid, state0, node, state, false, ap, config, localCc) and + ap0 instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + jumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStateStep(mid, state0, node, state, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + // store + exists(TypedContent tc, Ap ap0 | + fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + ap = apCons(tc, ap0) + ) + or + // read + exists(Ap ap0, Content c | + fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowConsCand(ap0, c, ap, config) + ) + or + // flow into a callable + exists(ApApprox apa | + fwdFlowIn(_, node, state, _, cc, _, ap, config) and + apa = getApprox(ap) and + if PrevStage::parameterMayFlowThrough(node, _, apa, config) + then argAp = apSome(ap) + else argAp = apNone() + ) + or + // flow out of a callable + fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) + ) + } - ApOption apNone() { result = TBooleanNone() } + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + exists(DataFlowType contentType | + fwdFlow(node1, state, cc, argAp, ap1, config) and + PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and + typecheckStore(ap1, contentType) + ) + } - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + /** + * Holds if forward flow with access path `tail` reaches a store of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(TypedContent tc | + fwdFlowStore(_, tail, tc, _, _, _, _, config) and + tc.getContent() = c and + cons = apCons(tc, tail) + ) + } + pragma[nomagic] + private predicate fwdFlowRead( + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + fwdFlow(node1, state, cc, argAp, ap, config) and + PrevStage::readStepCand(node1, c, node2, config) and + getHeadContent(ap) = c + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, + Ap ap, Configuration config + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, state, outercc, argAp, ap, config) and + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutNotFromArg( + NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + ) { + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, argAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + ccOut = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + ) + } + + pragma[nomagic] + private predicate storeStepFwd( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config + ) { + fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + ap2 = apCons(tc, ap1) and + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + } + + private predicate readStepFwd( + NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config + ) { + fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowConsCand(ap1, c, ap2, config) + } + + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and + fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), + pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + + pragma[nomagic] + private predicate returnNodeMayFlowThrough( + RetNodeEx ret, FlowState state, Ap ap, Configuration config + ) { + fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from the + * enclosing callable in order to reach a sink, and if so, `returnAp` records + * the access path of the returned value. + */ + pragma[nomagic] + predicate revFlow( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + revFlow0(node, state, toReturn, returnAp, ap, config) and + fwdFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + fwdFlow(node, state, _, _, ap, config) and + sinkNode(node, state, config) and + (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid, FlowState state0 | + localStep(node, state, mid, state0, true, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, ap, config) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, state, _, _, ap, config) and + toReturn = false and + returnAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStep(node, mid, config) and + revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStateStep(node, state, mid, state0, config) and + revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, + pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowConsCand(ap0, c, ap, config) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, state, toReturn, returnAp, ap0, config) and + readStepFwd(node, ap, _, mid, ap0, config) + ) + or + // flow into a callable + revFlowInNotToReturn(node, state, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + or + // flow out of a callable + revFlowOut(_, node, state, _, _, ap, config) and + toReturn = true and + if returnNodeMayFlowThrough(node, state, ap, config) + then returnAp = apSome(ap) + else returnAp = apNone() + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, + boolean toReturn, ApOption returnAp, Configuration config + ) { + revFlow(mid, state, toReturn, returnAp, ap0, config) and + storeStepFwd(node, ap, tc, mid, ap0, config) and + tc.getContent() = c + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, _, tail, config) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0, config) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, + Configuration config + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, state, toReturn, returnAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInNotToReturn( + ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, FlowState state, CcCall ccc | + revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and + fwdFlow(ret, state, ccc, apSome(_), ap, config) and + matchesCall(ccc, call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Ap ap2, Content c | + PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and + revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and + revFlowConsCand(ap2, c, ap1, config) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and + readStepFwd(node1, ap1, c, node2, ap2, config) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, + pragma[only_bind_into](config)) + ) + } + + predicate revFlow(NodeEx node, FlowState state, Configuration config) { + revFlow(node, state, _, _, _, config) + } + + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, ap, config) + } + + private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepFwd(_, ap, tc, _, _, config) + } + + private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepCand(_, ap, tc, _, _, config) + } + + private predicate validAp(Ap ap, Configuration config) { + revFlow(_, _, _, _, ap, config) and ap instanceof ApNil + or + exists(TypedContent head, Ap tail | + consCand(head, tail, config) and + ap = apCons(head, tail) + ) + } + + predicate consCand(TypedContent tc, Ap ap, Configuration config) { + revConsCand(tc, ap, config) and + validAp(ap, config) + } + + pragma[noinline] + private predicate parameterFlow( + ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ) { + revFlow(p, _, true, apSome(ap0), ap, config) and + c = p.getEnclosingCallable() + } + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | + parameterFlow(p, ap, ap0, c, config) and + c = ret.getEnclosingCallable() and + revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), + pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and + fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and + kind = ret.getKind() and + p.getPosition() = pos and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists( + Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + | + revFlow(arg, state, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + + predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, argAp, ap, config) + ) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | consCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and + states = count(FlowState state | revFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | + revFlow(n, state, b, retAp, ap, config) + ) + } + /* End: Stage logic. */ + } +} + +private module BooleanCallContext { + class Cc extends boolean { + Cc() { this in [true, false] } + } + + class CcCall extends Cc { + CcCall() { this = true } + } + + /** Holds if the call context may be `call`. */ + predicate matchesCall(CcCall cc, DataFlowCall call) { any() } + + class CcNoCall extends Cc { + CcNoCall() { this = false } + } + + Cc ccNone() { result = false } + + CcCall ccSomeCall() { result = true } + + class LocalCc = Unit; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } +} + +private module Level1CallContext { class Cc = CallContext; class CcCall = CallContextCall; + pragma[inline] + predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } + class CcNoCall = CallContextNoCall; Cc ccNone() { result instanceof CallContextAny } CcCall ccSomeCall() { result instanceof CallContextSomeCall } - private class LocalCc = Unit; + module NoLocalCallContext { + class LocalCc = Unit; - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSiteDispatch(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSiteDispatch(call, c) + then result = TSpecificCall(call) + else result = TSomeCall() + } + } + + module LocalCallContext { + class LocalCc = LocalCallContext; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { + result = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + node.getEnclosingCallable()) + } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() + } } bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { checkCallContextReturn(innercc, c, call) and if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() } +} - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } +private module Stage2Param implements MkStage::StageParam { + private module PrevStage = Stage1; + + class Ap extends boolean { + Ap() { this in [true, false] } + } + + class ApNil extends Ap { + ApNil() { this = false } + } + + bindingset[result, ap] + PrevStage::Ap getApprox(Ap ap) { any() } + + ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + + pragma[inline] + Content getHeadContent(Ap ap) { exists(result) and ap = true } + + class ApOption = BooleanOption; + + ApOption apNone() { result = TBooleanNone() } + + ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + + import Level1CallContext + import NoLocalCallContext bindingset[node1, state1, config] bindingset[node2, state2, config] - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -1221,9 +1906,9 @@ private module Stage2 { exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/5; - private predicate flowIntoCall = flowIntoCallNodeCand1/5; + predicate flowIntoCall = flowIntoCallNodeCand1/5; pragma[nomagic] private predicate expectsContentCand(NodeEx node, Configuration config) { @@ -1235,7 +1920,7 @@ private module Stage2 { } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and not stateBarrier(node, state, config) and @@ -1248,544 +1933,11 @@ private module Stage2 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 2 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 2 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage2 = MkStage::Stage; + pragma[nomagic] private predicate flowOutOfCallNodeCand2( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config @@ -1883,14 +2035,13 @@ private module LocalFlowBigStep { ) { additionalLocalFlowStepNodeCand1(node1, node2, config) and state1 = state2 and - Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false, - pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false, + Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false, pragma[only_bind_into](config)) or additionalLocalStateStep(node1, state1, node2, state2, config) and - Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config)) + Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config)) } /** @@ -1967,26 +2118,24 @@ private module LocalFlowBigStep { private import LocalFlowBigStep -private module Stage3 { - module PrevStage = Stage2; - - class ApApprox = PrevStage::Ap; +private module Stage3Param implements MkStage::StageParam { + private module PrevStage = Stage2; class Ap = AccessPathFront; class ApNil = AccessPathFrontNil; - private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } + PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } + Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathFrontOption; @@ -1994,44 +2143,18 @@ private module Stage3 { ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - class Cc = boolean; + import BooleanCallContext - class CcCall extends Cc { - CcCall() { this = true } - - /** Holds if this call context may be `call`. */ - predicate matchesCall(DataFlowCall call) { any() } - } - - class CcNoCall extends Cc { - CcNoCall() { this = false } - } - - Cc ccNone() { result = false } - - CcCall ccSomeCall() { result = true } - - private class LocalCc = Unit; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/5; - private predicate flowIntoCall = flowIntoCallNodeCand2/5; + predicate flowIntoCall = flowIntoCallNodeCand2/5; pragma[nomagic] private predicate clearSet(NodeEx node, ContentSet c, Configuration config) { @@ -2067,7 +2190,7 @@ private module Stage3 { private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { exists(state) and exists(config) and not clear(node, ap, config) and @@ -2080,548 +2203,15 @@ private module Stage3 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { + predicate typecheckStore(Ap ap, DataFlowType contentType) { // We need to typecheck stores here, since reverse flow through a getter // might have a different type here compared to inside the getter. compatibleTypes(ap.getType(), contentType) } - - /* Begin: Stage 3 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 3 logic. */ } +private module Stage3 = MkStage::Stage; + /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` * and remains relevant for the following pruning stage. @@ -2644,7 +2234,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and nodes = strictcount(NodeEx n, FlowState state | - Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) or flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) ) and @@ -2828,26 +2418,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } } -private module Stage4 { - module PrevStage = Stage3; - - class ApApprox = PrevStage::Ap; +private module Stage4Param implements MkStage::StageParam { + private module PrevStage = Stage3; class Ap = AccessPathApprox; class ApNil = AccessPathApproxNil; - private ApApprox getApprox(Ap ap) { result = ap.getFront() } + PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } + Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathApproxOption; @@ -2855,38 +2443,10 @@ private module Stage4 { ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - class Cc = CallContext; + import Level1CallContext + import LocalCallContext - class CcCall = CallContextCall; - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - private class LocalCc = LocalCallContext; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() - } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { - result = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - node.getEnclosingCallable()) - } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -2894,575 +2454,40 @@ private module Stage4 { } pragma[nomagic] - private predicate flowOutOfCall( + predicate flowOutOfCall( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } pragma[nomagic] - private predicate flowIntoCall( + predicate flowIntoCall( DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } // Type checking is not necessary here as it has already been done in stage 3. bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 4 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 4 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage4 = MkStage::Stage; + bindingset[conf, result] private Configuration unbindConf(Configuration conf) { exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) @@ -3495,7 +2520,7 @@ private newtype TSummaryCtx = TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and - Stage4::revFlow(p, state, _, _, _, config) + Stage4::revFlow(p, state, _, config) ) } @@ -3553,7 +2578,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { result = strictcount(NodeEx n, FlowState state | - Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config) + Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config) ) } @@ -3667,7 +2692,7 @@ private newtype TPathNode = exists(PathNodeMid mid | pathStep(mid, node, state, cc, sc, ap) and pragma[only_bind_into](config) = mid.getConfiguration() and - Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config)) + Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config)) ) } or TPathNodeSink(NodeEx node, FlowState state, Configuration config) { @@ -4207,7 +3232,7 @@ private NodeEx getAnOutNodeFlow( ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result.asNode() = kind.getAnOutNode(call) and - Stage4::revFlow(result, _, _, _, apa, config) + Stage4::revFlow(result, _, apa, config) } /** @@ -4243,7 +3268,7 @@ private predicate parameterCand( DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config ) { exists(ParamNodeEx p | - Stage4::revFlow(p, _, _, _, apa, config) and + Stage4::revFlow(p, _, apa, config) and p.isParameterOf(callable, pos) ) } diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll index a076f7a2e45..22c3bd2466e 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,12 +944,9 @@ private module Stage1 { predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) } bindingset[node, state, config] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, toReturn, pragma[only_bind_into](config)) and + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, _, pragma[only_bind_into](config)) and exists(state) and - exists(returnAp) and exists(ap) } @@ -1142,66 +1139,754 @@ private predicate flowIntoCallNodeCand1( ) } -private module Stage2 { - module PrevStage = Stage1; +private signature module StageSig { + class Ap; + predicate revFlow(NodeEx node, Configuration config); + + bindingset[node, state, config] + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config); + + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ); + + predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config); +} + +private module MkStage { class ApApprox = PrevStage::Ap; - class Ap = boolean; + signature module StageParam { + class Ap; - class ApNil extends Ap { - ApNil() { this = false } + class ApNil extends Ap; + + bindingset[result, ap] + ApApprox getApprox(Ap ap); + + ApNil getApNil(NodeEx node); + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail); + + Content getHeadContent(Ap ap); + + class ApOption; + + ApOption apNone(); + + ApOption apSome(Ap ap); + + class Cc; + + class CcCall extends Cc; + + // TODO: member predicate on CcCall + predicate matchesCall(CcCall cc, DataFlowCall call); + + class CcNoCall extends Cc; + + Cc ccNone(); + + CcCall ccSomeCall(); + + class LocalCc; + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc); + + bindingset[node1, state1, config] + bindingset[node2, state2, config] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ); + + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + ); + + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ); + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config); + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType); } - bindingset[result, ap] - private ApApprox getApprox(Ap ap) { any() } + module Stage implements StageSig { + import Param - private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) } + /* Begin: Stage logic. */ + bindingset[result, apa] + private ApApprox unbindApa(ApApprox apa) { + pragma[only_bind_out](apa) = pragma[only_bind_out](result) + } - bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, + Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, + pragma[only_bind_into](config)) and + matchesCall(ccc, call) + } - pragma[inline] - private Content getHeadContent(Ap ap) { exists(result) and ap = true } + /** + * Holds if `node` is reachable with access path `ap` from a source in the + * configuration `config`. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. + */ + pragma[nomagic] + predicate fwdFlow( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + fwdFlow0(node, state, cc, argAp, ap, config) and + PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and + filter(node, state, ap, config) + } - class ApOption = BooleanOption; + pragma[nomagic] + private predicate fwdFlow0( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + sourceNode(node, state, config) and + (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and + argAp = apNone() and + ap = getApNil(node) + or + exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | + fwdFlow(mid, state0, cc, argAp, ap0, config) and + localCc = getLocalCc(mid, cc) + | + localStep(mid, state0, node, state, true, _, config, localCc) and + ap = ap0 + or + localStep(mid, state0, node, state, false, ap, config, localCc) and + ap0 instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + jumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStateStep(mid, state0, node, state, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + // store + exists(TypedContent tc, Ap ap0 | + fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + ap = apCons(tc, ap0) + ) + or + // read + exists(Ap ap0, Content c | + fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowConsCand(ap0, c, ap, config) + ) + or + // flow into a callable + exists(ApApprox apa | + fwdFlowIn(_, node, state, _, cc, _, ap, config) and + apa = getApprox(ap) and + if PrevStage::parameterMayFlowThrough(node, _, apa, config) + then argAp = apSome(ap) + else argAp = apNone() + ) + or + // flow out of a callable + fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) + ) + } - ApOption apNone() { result = TBooleanNone() } + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + exists(DataFlowType contentType | + fwdFlow(node1, state, cc, argAp, ap1, config) and + PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and + typecheckStore(ap1, contentType) + ) + } - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + /** + * Holds if forward flow with access path `tail` reaches a store of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(TypedContent tc | + fwdFlowStore(_, tail, tc, _, _, _, _, config) and + tc.getContent() = c and + cons = apCons(tc, tail) + ) + } + pragma[nomagic] + private predicate fwdFlowRead( + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + fwdFlow(node1, state, cc, argAp, ap, config) and + PrevStage::readStepCand(node1, c, node2, config) and + getHeadContent(ap) = c + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, + Ap ap, Configuration config + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, state, outercc, argAp, ap, config) and + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutNotFromArg( + NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + ) { + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, argAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + ccOut = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + ) + } + + pragma[nomagic] + private predicate storeStepFwd( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config + ) { + fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + ap2 = apCons(tc, ap1) and + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + } + + private predicate readStepFwd( + NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config + ) { + fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowConsCand(ap1, c, ap2, config) + } + + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and + fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), + pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + + pragma[nomagic] + private predicate returnNodeMayFlowThrough( + RetNodeEx ret, FlowState state, Ap ap, Configuration config + ) { + fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from the + * enclosing callable in order to reach a sink, and if so, `returnAp` records + * the access path of the returned value. + */ + pragma[nomagic] + predicate revFlow( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + revFlow0(node, state, toReturn, returnAp, ap, config) and + fwdFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + fwdFlow(node, state, _, _, ap, config) and + sinkNode(node, state, config) and + (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid, FlowState state0 | + localStep(node, state, mid, state0, true, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, ap, config) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, state, _, _, ap, config) and + toReturn = false and + returnAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStep(node, mid, config) and + revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStateStep(node, state, mid, state0, config) and + revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, + pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowConsCand(ap0, c, ap, config) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, state, toReturn, returnAp, ap0, config) and + readStepFwd(node, ap, _, mid, ap0, config) + ) + or + // flow into a callable + revFlowInNotToReturn(node, state, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + or + // flow out of a callable + revFlowOut(_, node, state, _, _, ap, config) and + toReturn = true and + if returnNodeMayFlowThrough(node, state, ap, config) + then returnAp = apSome(ap) + else returnAp = apNone() + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, + boolean toReturn, ApOption returnAp, Configuration config + ) { + revFlow(mid, state, toReturn, returnAp, ap0, config) and + storeStepFwd(node, ap, tc, mid, ap0, config) and + tc.getContent() = c + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, _, tail, config) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0, config) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, + Configuration config + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, state, toReturn, returnAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInNotToReturn( + ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, FlowState state, CcCall ccc | + revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and + fwdFlow(ret, state, ccc, apSome(_), ap, config) and + matchesCall(ccc, call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Ap ap2, Content c | + PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and + revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and + revFlowConsCand(ap2, c, ap1, config) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and + readStepFwd(node1, ap1, c, node2, ap2, config) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, + pragma[only_bind_into](config)) + ) + } + + predicate revFlow(NodeEx node, FlowState state, Configuration config) { + revFlow(node, state, _, _, _, config) + } + + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, ap, config) + } + + private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepFwd(_, ap, tc, _, _, config) + } + + private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepCand(_, ap, tc, _, _, config) + } + + private predicate validAp(Ap ap, Configuration config) { + revFlow(_, _, _, _, ap, config) and ap instanceof ApNil + or + exists(TypedContent head, Ap tail | + consCand(head, tail, config) and + ap = apCons(head, tail) + ) + } + + predicate consCand(TypedContent tc, Ap ap, Configuration config) { + revConsCand(tc, ap, config) and + validAp(ap, config) + } + + pragma[noinline] + private predicate parameterFlow( + ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ) { + revFlow(p, _, true, apSome(ap0), ap, config) and + c = p.getEnclosingCallable() + } + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | + parameterFlow(p, ap, ap0, c, config) and + c = ret.getEnclosingCallable() and + revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), + pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and + fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and + kind = ret.getKind() and + p.getPosition() = pos and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists( + Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + | + revFlow(arg, state, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + + predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, argAp, ap, config) + ) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | consCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and + states = count(FlowState state | revFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | + revFlow(n, state, b, retAp, ap, config) + ) + } + /* End: Stage logic. */ + } +} + +private module BooleanCallContext { + class Cc extends boolean { + Cc() { this in [true, false] } + } + + class CcCall extends Cc { + CcCall() { this = true } + } + + /** Holds if the call context may be `call`. */ + predicate matchesCall(CcCall cc, DataFlowCall call) { any() } + + class CcNoCall extends Cc { + CcNoCall() { this = false } + } + + Cc ccNone() { result = false } + + CcCall ccSomeCall() { result = true } + + class LocalCc = Unit; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } +} + +private module Level1CallContext { class Cc = CallContext; class CcCall = CallContextCall; + pragma[inline] + predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } + class CcNoCall = CallContextNoCall; Cc ccNone() { result instanceof CallContextAny } CcCall ccSomeCall() { result instanceof CallContextSomeCall } - private class LocalCc = Unit; + module NoLocalCallContext { + class LocalCc = Unit; - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSiteDispatch(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSiteDispatch(call, c) + then result = TSpecificCall(call) + else result = TSomeCall() + } + } + + module LocalCallContext { + class LocalCc = LocalCallContext; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { + result = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + node.getEnclosingCallable()) + } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() + } } bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { checkCallContextReturn(innercc, c, call) and if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() } +} - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } +private module Stage2Param implements MkStage::StageParam { + private module PrevStage = Stage1; + + class Ap extends boolean { + Ap() { this in [true, false] } + } + + class ApNil extends Ap { + ApNil() { this = false } + } + + bindingset[result, ap] + PrevStage::Ap getApprox(Ap ap) { any() } + + ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + + pragma[inline] + Content getHeadContent(Ap ap) { exists(result) and ap = true } + + class ApOption = BooleanOption; + + ApOption apNone() { result = TBooleanNone() } + + ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + + import Level1CallContext + import NoLocalCallContext bindingset[node1, state1, config] bindingset[node2, state2, config] - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -1221,9 +1906,9 @@ private module Stage2 { exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/5; - private predicate flowIntoCall = flowIntoCallNodeCand1/5; + predicate flowIntoCall = flowIntoCallNodeCand1/5; pragma[nomagic] private predicate expectsContentCand(NodeEx node, Configuration config) { @@ -1235,7 +1920,7 @@ private module Stage2 { } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and not stateBarrier(node, state, config) and @@ -1248,544 +1933,11 @@ private module Stage2 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 2 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 2 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage2 = MkStage::Stage; + pragma[nomagic] private predicate flowOutOfCallNodeCand2( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config @@ -1883,14 +2035,13 @@ private module LocalFlowBigStep { ) { additionalLocalFlowStepNodeCand1(node1, node2, config) and state1 = state2 and - Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false, - pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false, + Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false, pragma[only_bind_into](config)) or additionalLocalStateStep(node1, state1, node2, state2, config) and - Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config)) + Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config)) } /** @@ -1967,26 +2118,24 @@ private module LocalFlowBigStep { private import LocalFlowBigStep -private module Stage3 { - module PrevStage = Stage2; - - class ApApprox = PrevStage::Ap; +private module Stage3Param implements MkStage::StageParam { + private module PrevStage = Stage2; class Ap = AccessPathFront; class ApNil = AccessPathFrontNil; - private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } + PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } + Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathFrontOption; @@ -1994,44 +2143,18 @@ private module Stage3 { ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - class Cc = boolean; + import BooleanCallContext - class CcCall extends Cc { - CcCall() { this = true } - - /** Holds if this call context may be `call`. */ - predicate matchesCall(DataFlowCall call) { any() } - } - - class CcNoCall extends Cc { - CcNoCall() { this = false } - } - - Cc ccNone() { result = false } - - CcCall ccSomeCall() { result = true } - - private class LocalCc = Unit; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/5; - private predicate flowIntoCall = flowIntoCallNodeCand2/5; + predicate flowIntoCall = flowIntoCallNodeCand2/5; pragma[nomagic] private predicate clearSet(NodeEx node, ContentSet c, Configuration config) { @@ -2067,7 +2190,7 @@ private module Stage3 { private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { exists(state) and exists(config) and not clear(node, ap, config) and @@ -2080,548 +2203,15 @@ private module Stage3 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { + predicate typecheckStore(Ap ap, DataFlowType contentType) { // We need to typecheck stores here, since reverse flow through a getter // might have a different type here compared to inside the getter. compatibleTypes(ap.getType(), contentType) } - - /* Begin: Stage 3 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 3 logic. */ } +private module Stage3 = MkStage::Stage; + /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` * and remains relevant for the following pruning stage. @@ -2644,7 +2234,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and nodes = strictcount(NodeEx n, FlowState state | - Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) or flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) ) and @@ -2828,26 +2418,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } } -private module Stage4 { - module PrevStage = Stage3; - - class ApApprox = PrevStage::Ap; +private module Stage4Param implements MkStage::StageParam { + private module PrevStage = Stage3; class Ap = AccessPathApprox; class ApNil = AccessPathApproxNil; - private ApApprox getApprox(Ap ap) { result = ap.getFront() } + PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } + Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathApproxOption; @@ -2855,38 +2443,10 @@ private module Stage4 { ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - class Cc = CallContext; + import Level1CallContext + import LocalCallContext - class CcCall = CallContextCall; - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - private class LocalCc = LocalCallContext; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() - } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { - result = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - node.getEnclosingCallable()) - } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -2894,575 +2454,40 @@ private module Stage4 { } pragma[nomagic] - private predicate flowOutOfCall( + predicate flowOutOfCall( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } pragma[nomagic] - private predicate flowIntoCall( + predicate flowIntoCall( DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } // Type checking is not necessary here as it has already been done in stage 3. bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 4 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 4 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage4 = MkStage::Stage; + bindingset[conf, result] private Configuration unbindConf(Configuration conf) { exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) @@ -3495,7 +2520,7 @@ private newtype TSummaryCtx = TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and - Stage4::revFlow(p, state, _, _, _, config) + Stage4::revFlow(p, state, _, config) ) } @@ -3553,7 +2578,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { result = strictcount(NodeEx n, FlowState state | - Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config) + Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config) ) } @@ -3667,7 +2692,7 @@ private newtype TPathNode = exists(PathNodeMid mid | pathStep(mid, node, state, cc, sc, ap) and pragma[only_bind_into](config) = mid.getConfiguration() and - Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config)) + Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config)) ) } or TPathNodeSink(NodeEx node, FlowState state, Configuration config) { @@ -4207,7 +3232,7 @@ private NodeEx getAnOutNodeFlow( ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result.asNode() = kind.getAnOutNode(call) and - Stage4::revFlow(result, _, _, _, apa, config) + Stage4::revFlow(result, _, apa, config) } /** @@ -4243,7 +3268,7 @@ private predicate parameterCand( DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config ) { exists(ParamNodeEx p | - Stage4::revFlow(p, _, _, _, apa, config) and + Stage4::revFlow(p, _, apa, config) and p.isParameterOf(callable, pos) ) } diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll index a076f7a2e45..22c3bd2466e 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,12 +944,9 @@ private module Stage1 { predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) } bindingset[node, state, config] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, toReturn, pragma[only_bind_into](config)) and + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, _, pragma[only_bind_into](config)) and exists(state) and - exists(returnAp) and exists(ap) } @@ -1142,66 +1139,754 @@ private predicate flowIntoCallNodeCand1( ) } -private module Stage2 { - module PrevStage = Stage1; +private signature module StageSig { + class Ap; + predicate revFlow(NodeEx node, Configuration config); + + bindingset[node, state, config] + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config); + + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ); + + predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config); +} + +private module MkStage { class ApApprox = PrevStage::Ap; - class Ap = boolean; + signature module StageParam { + class Ap; - class ApNil extends Ap { - ApNil() { this = false } + class ApNil extends Ap; + + bindingset[result, ap] + ApApprox getApprox(Ap ap); + + ApNil getApNil(NodeEx node); + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail); + + Content getHeadContent(Ap ap); + + class ApOption; + + ApOption apNone(); + + ApOption apSome(Ap ap); + + class Cc; + + class CcCall extends Cc; + + // TODO: member predicate on CcCall + predicate matchesCall(CcCall cc, DataFlowCall call); + + class CcNoCall extends Cc; + + Cc ccNone(); + + CcCall ccSomeCall(); + + class LocalCc; + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc); + + bindingset[node1, state1, config] + bindingset[node2, state2, config] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ); + + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + ); + + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ); + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config); + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType); } - bindingset[result, ap] - private ApApprox getApprox(Ap ap) { any() } + module Stage implements StageSig { + import Param - private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) } + /* Begin: Stage logic. */ + bindingset[result, apa] + private ApApprox unbindApa(ApApprox apa) { + pragma[only_bind_out](apa) = pragma[only_bind_out](result) + } - bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, + Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, + pragma[only_bind_into](config)) and + matchesCall(ccc, call) + } - pragma[inline] - private Content getHeadContent(Ap ap) { exists(result) and ap = true } + /** + * Holds if `node` is reachable with access path `ap` from a source in the + * configuration `config`. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. + */ + pragma[nomagic] + predicate fwdFlow( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + fwdFlow0(node, state, cc, argAp, ap, config) and + PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and + filter(node, state, ap, config) + } - class ApOption = BooleanOption; + pragma[nomagic] + private predicate fwdFlow0( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + sourceNode(node, state, config) and + (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and + argAp = apNone() and + ap = getApNil(node) + or + exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | + fwdFlow(mid, state0, cc, argAp, ap0, config) and + localCc = getLocalCc(mid, cc) + | + localStep(mid, state0, node, state, true, _, config, localCc) and + ap = ap0 + or + localStep(mid, state0, node, state, false, ap, config, localCc) and + ap0 instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + jumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStateStep(mid, state0, node, state, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + // store + exists(TypedContent tc, Ap ap0 | + fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + ap = apCons(tc, ap0) + ) + or + // read + exists(Ap ap0, Content c | + fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowConsCand(ap0, c, ap, config) + ) + or + // flow into a callable + exists(ApApprox apa | + fwdFlowIn(_, node, state, _, cc, _, ap, config) and + apa = getApprox(ap) and + if PrevStage::parameterMayFlowThrough(node, _, apa, config) + then argAp = apSome(ap) + else argAp = apNone() + ) + or + // flow out of a callable + fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) + ) + } - ApOption apNone() { result = TBooleanNone() } + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + exists(DataFlowType contentType | + fwdFlow(node1, state, cc, argAp, ap1, config) and + PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and + typecheckStore(ap1, contentType) + ) + } - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + /** + * Holds if forward flow with access path `tail` reaches a store of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(TypedContent tc | + fwdFlowStore(_, tail, tc, _, _, _, _, config) and + tc.getContent() = c and + cons = apCons(tc, tail) + ) + } + pragma[nomagic] + private predicate fwdFlowRead( + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + fwdFlow(node1, state, cc, argAp, ap, config) and + PrevStage::readStepCand(node1, c, node2, config) and + getHeadContent(ap) = c + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, + Ap ap, Configuration config + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, state, outercc, argAp, ap, config) and + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutNotFromArg( + NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + ) { + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, argAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + ccOut = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + ) + } + + pragma[nomagic] + private predicate storeStepFwd( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config + ) { + fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + ap2 = apCons(tc, ap1) and + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + } + + private predicate readStepFwd( + NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config + ) { + fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowConsCand(ap1, c, ap2, config) + } + + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and + fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), + pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + + pragma[nomagic] + private predicate returnNodeMayFlowThrough( + RetNodeEx ret, FlowState state, Ap ap, Configuration config + ) { + fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from the + * enclosing callable in order to reach a sink, and if so, `returnAp` records + * the access path of the returned value. + */ + pragma[nomagic] + predicate revFlow( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + revFlow0(node, state, toReturn, returnAp, ap, config) and + fwdFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + fwdFlow(node, state, _, _, ap, config) and + sinkNode(node, state, config) and + (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid, FlowState state0 | + localStep(node, state, mid, state0, true, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, ap, config) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, state, _, _, ap, config) and + toReturn = false and + returnAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStep(node, mid, config) and + revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStateStep(node, state, mid, state0, config) and + revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, + pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowConsCand(ap0, c, ap, config) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, state, toReturn, returnAp, ap0, config) and + readStepFwd(node, ap, _, mid, ap0, config) + ) + or + // flow into a callable + revFlowInNotToReturn(node, state, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + or + // flow out of a callable + revFlowOut(_, node, state, _, _, ap, config) and + toReturn = true and + if returnNodeMayFlowThrough(node, state, ap, config) + then returnAp = apSome(ap) + else returnAp = apNone() + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, + boolean toReturn, ApOption returnAp, Configuration config + ) { + revFlow(mid, state, toReturn, returnAp, ap0, config) and + storeStepFwd(node, ap, tc, mid, ap0, config) and + tc.getContent() = c + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, _, tail, config) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0, config) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, + Configuration config + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, state, toReturn, returnAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInNotToReturn( + ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, FlowState state, CcCall ccc | + revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and + fwdFlow(ret, state, ccc, apSome(_), ap, config) and + matchesCall(ccc, call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Ap ap2, Content c | + PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and + revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and + revFlowConsCand(ap2, c, ap1, config) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and + readStepFwd(node1, ap1, c, node2, ap2, config) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, + pragma[only_bind_into](config)) + ) + } + + predicate revFlow(NodeEx node, FlowState state, Configuration config) { + revFlow(node, state, _, _, _, config) + } + + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, ap, config) + } + + private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepFwd(_, ap, tc, _, _, config) + } + + private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepCand(_, ap, tc, _, _, config) + } + + private predicate validAp(Ap ap, Configuration config) { + revFlow(_, _, _, _, ap, config) and ap instanceof ApNil + or + exists(TypedContent head, Ap tail | + consCand(head, tail, config) and + ap = apCons(head, tail) + ) + } + + predicate consCand(TypedContent tc, Ap ap, Configuration config) { + revConsCand(tc, ap, config) and + validAp(ap, config) + } + + pragma[noinline] + private predicate parameterFlow( + ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ) { + revFlow(p, _, true, apSome(ap0), ap, config) and + c = p.getEnclosingCallable() + } + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | + parameterFlow(p, ap, ap0, c, config) and + c = ret.getEnclosingCallable() and + revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), + pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and + fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and + kind = ret.getKind() and + p.getPosition() = pos and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists( + Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + | + revFlow(arg, state, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + + predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, argAp, ap, config) + ) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | consCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and + states = count(FlowState state | revFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | + revFlow(n, state, b, retAp, ap, config) + ) + } + /* End: Stage logic. */ + } +} + +private module BooleanCallContext { + class Cc extends boolean { + Cc() { this in [true, false] } + } + + class CcCall extends Cc { + CcCall() { this = true } + } + + /** Holds if the call context may be `call`. */ + predicate matchesCall(CcCall cc, DataFlowCall call) { any() } + + class CcNoCall extends Cc { + CcNoCall() { this = false } + } + + Cc ccNone() { result = false } + + CcCall ccSomeCall() { result = true } + + class LocalCc = Unit; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } +} + +private module Level1CallContext { class Cc = CallContext; class CcCall = CallContextCall; + pragma[inline] + predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } + class CcNoCall = CallContextNoCall; Cc ccNone() { result instanceof CallContextAny } CcCall ccSomeCall() { result instanceof CallContextSomeCall } - private class LocalCc = Unit; + module NoLocalCallContext { + class LocalCc = Unit; - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSiteDispatch(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSiteDispatch(call, c) + then result = TSpecificCall(call) + else result = TSomeCall() + } + } + + module LocalCallContext { + class LocalCc = LocalCallContext; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { + result = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + node.getEnclosingCallable()) + } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() + } } bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { checkCallContextReturn(innercc, c, call) and if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() } +} - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } +private module Stage2Param implements MkStage::StageParam { + private module PrevStage = Stage1; + + class Ap extends boolean { + Ap() { this in [true, false] } + } + + class ApNil extends Ap { + ApNil() { this = false } + } + + bindingset[result, ap] + PrevStage::Ap getApprox(Ap ap) { any() } + + ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + + pragma[inline] + Content getHeadContent(Ap ap) { exists(result) and ap = true } + + class ApOption = BooleanOption; + + ApOption apNone() { result = TBooleanNone() } + + ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + + import Level1CallContext + import NoLocalCallContext bindingset[node1, state1, config] bindingset[node2, state2, config] - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -1221,9 +1906,9 @@ private module Stage2 { exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/5; - private predicate flowIntoCall = flowIntoCallNodeCand1/5; + predicate flowIntoCall = flowIntoCallNodeCand1/5; pragma[nomagic] private predicate expectsContentCand(NodeEx node, Configuration config) { @@ -1235,7 +1920,7 @@ private module Stage2 { } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and not stateBarrier(node, state, config) and @@ -1248,544 +1933,11 @@ private module Stage2 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 2 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 2 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage2 = MkStage::Stage; + pragma[nomagic] private predicate flowOutOfCallNodeCand2( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config @@ -1883,14 +2035,13 @@ private module LocalFlowBigStep { ) { additionalLocalFlowStepNodeCand1(node1, node2, config) and state1 = state2 and - Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false, - pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false, + Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false, pragma[only_bind_into](config)) or additionalLocalStateStep(node1, state1, node2, state2, config) and - Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config)) + Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config)) } /** @@ -1967,26 +2118,24 @@ private module LocalFlowBigStep { private import LocalFlowBigStep -private module Stage3 { - module PrevStage = Stage2; - - class ApApprox = PrevStage::Ap; +private module Stage3Param implements MkStage::StageParam { + private module PrevStage = Stage2; class Ap = AccessPathFront; class ApNil = AccessPathFrontNil; - private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } + PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } + Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathFrontOption; @@ -1994,44 +2143,18 @@ private module Stage3 { ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - class Cc = boolean; + import BooleanCallContext - class CcCall extends Cc { - CcCall() { this = true } - - /** Holds if this call context may be `call`. */ - predicate matchesCall(DataFlowCall call) { any() } - } - - class CcNoCall extends Cc { - CcNoCall() { this = false } - } - - Cc ccNone() { result = false } - - CcCall ccSomeCall() { result = true } - - private class LocalCc = Unit; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/5; - private predicate flowIntoCall = flowIntoCallNodeCand2/5; + predicate flowIntoCall = flowIntoCallNodeCand2/5; pragma[nomagic] private predicate clearSet(NodeEx node, ContentSet c, Configuration config) { @@ -2067,7 +2190,7 @@ private module Stage3 { private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { exists(state) and exists(config) and not clear(node, ap, config) and @@ -2080,548 +2203,15 @@ private module Stage3 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { + predicate typecheckStore(Ap ap, DataFlowType contentType) { // We need to typecheck stores here, since reverse flow through a getter // might have a different type here compared to inside the getter. compatibleTypes(ap.getType(), contentType) } - - /* Begin: Stage 3 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 3 logic. */ } +private module Stage3 = MkStage::Stage; + /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` * and remains relevant for the following pruning stage. @@ -2644,7 +2234,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and nodes = strictcount(NodeEx n, FlowState state | - Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) or flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) ) and @@ -2828,26 +2418,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } } -private module Stage4 { - module PrevStage = Stage3; - - class ApApprox = PrevStage::Ap; +private module Stage4Param implements MkStage::StageParam { + private module PrevStage = Stage3; class Ap = AccessPathApprox; class ApNil = AccessPathApproxNil; - private ApApprox getApprox(Ap ap) { result = ap.getFront() } + PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } + Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathApproxOption; @@ -2855,38 +2443,10 @@ private module Stage4 { ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - class Cc = CallContext; + import Level1CallContext + import LocalCallContext - class CcCall = CallContextCall; - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - private class LocalCc = LocalCallContext; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() - } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { - result = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - node.getEnclosingCallable()) - } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -2894,575 +2454,40 @@ private module Stage4 { } pragma[nomagic] - private predicate flowOutOfCall( + predicate flowOutOfCall( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } pragma[nomagic] - private predicate flowIntoCall( + predicate flowIntoCall( DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } // Type checking is not necessary here as it has already been done in stage 3. bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 4 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 4 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage4 = MkStage::Stage; + bindingset[conf, result] private Configuration unbindConf(Configuration conf) { exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) @@ -3495,7 +2520,7 @@ private newtype TSummaryCtx = TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and - Stage4::revFlow(p, state, _, _, _, config) + Stage4::revFlow(p, state, _, config) ) } @@ -3553,7 +2578,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { result = strictcount(NodeEx n, FlowState state | - Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config) + Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config) ) } @@ -3667,7 +2692,7 @@ private newtype TPathNode = exists(PathNodeMid mid | pathStep(mid, node, state, cc, sc, ap) and pragma[only_bind_into](config) = mid.getConfiguration() and - Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config)) + Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config)) ) } or TPathNodeSink(NodeEx node, FlowState state, Configuration config) { @@ -4207,7 +3232,7 @@ private NodeEx getAnOutNodeFlow( ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result.asNode() = kind.getAnOutNode(call) and - Stage4::revFlow(result, _, _, _, apa, config) + Stage4::revFlow(result, _, apa, config) } /** @@ -4243,7 +3268,7 @@ private predicate parameterCand( DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config ) { exists(ParamNodeEx p | - Stage4::revFlow(p, _, _, _, apa, config) and + Stage4::revFlow(p, _, apa, config) and p.isParameterOf(callable, pos) ) } diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll index a076f7a2e45..22c3bd2466e 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,12 +944,9 @@ private module Stage1 { predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) } bindingset[node, state, config] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, toReturn, pragma[only_bind_into](config)) and + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, _, pragma[only_bind_into](config)) and exists(state) and - exists(returnAp) and exists(ap) } @@ -1142,66 +1139,754 @@ private predicate flowIntoCallNodeCand1( ) } -private module Stage2 { - module PrevStage = Stage1; +private signature module StageSig { + class Ap; + predicate revFlow(NodeEx node, Configuration config); + + bindingset[node, state, config] + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config); + + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ); + + predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config); +} + +private module MkStage { class ApApprox = PrevStage::Ap; - class Ap = boolean; + signature module StageParam { + class Ap; - class ApNil extends Ap { - ApNil() { this = false } + class ApNil extends Ap; + + bindingset[result, ap] + ApApprox getApprox(Ap ap); + + ApNil getApNil(NodeEx node); + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail); + + Content getHeadContent(Ap ap); + + class ApOption; + + ApOption apNone(); + + ApOption apSome(Ap ap); + + class Cc; + + class CcCall extends Cc; + + // TODO: member predicate on CcCall + predicate matchesCall(CcCall cc, DataFlowCall call); + + class CcNoCall extends Cc; + + Cc ccNone(); + + CcCall ccSomeCall(); + + class LocalCc; + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc); + + bindingset[node1, state1, config] + bindingset[node2, state2, config] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ); + + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + ); + + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ); + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config); + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType); } - bindingset[result, ap] - private ApApprox getApprox(Ap ap) { any() } + module Stage implements StageSig { + import Param - private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) } + /* Begin: Stage logic. */ + bindingset[result, apa] + private ApApprox unbindApa(ApApprox apa) { + pragma[only_bind_out](apa) = pragma[only_bind_out](result) + } - bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, + Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, + pragma[only_bind_into](config)) and + matchesCall(ccc, call) + } - pragma[inline] - private Content getHeadContent(Ap ap) { exists(result) and ap = true } + /** + * Holds if `node` is reachable with access path `ap` from a source in the + * configuration `config`. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. + */ + pragma[nomagic] + predicate fwdFlow( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + fwdFlow0(node, state, cc, argAp, ap, config) and + PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and + filter(node, state, ap, config) + } - class ApOption = BooleanOption; + pragma[nomagic] + private predicate fwdFlow0( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + sourceNode(node, state, config) and + (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and + argAp = apNone() and + ap = getApNil(node) + or + exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | + fwdFlow(mid, state0, cc, argAp, ap0, config) and + localCc = getLocalCc(mid, cc) + | + localStep(mid, state0, node, state, true, _, config, localCc) and + ap = ap0 + or + localStep(mid, state0, node, state, false, ap, config, localCc) and + ap0 instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + jumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStateStep(mid, state0, node, state, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + // store + exists(TypedContent tc, Ap ap0 | + fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + ap = apCons(tc, ap0) + ) + or + // read + exists(Ap ap0, Content c | + fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowConsCand(ap0, c, ap, config) + ) + or + // flow into a callable + exists(ApApprox apa | + fwdFlowIn(_, node, state, _, cc, _, ap, config) and + apa = getApprox(ap) and + if PrevStage::parameterMayFlowThrough(node, _, apa, config) + then argAp = apSome(ap) + else argAp = apNone() + ) + or + // flow out of a callable + fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) + ) + } - ApOption apNone() { result = TBooleanNone() } + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + exists(DataFlowType contentType | + fwdFlow(node1, state, cc, argAp, ap1, config) and + PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and + typecheckStore(ap1, contentType) + ) + } - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + /** + * Holds if forward flow with access path `tail` reaches a store of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(TypedContent tc | + fwdFlowStore(_, tail, tc, _, _, _, _, config) and + tc.getContent() = c and + cons = apCons(tc, tail) + ) + } + pragma[nomagic] + private predicate fwdFlowRead( + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + fwdFlow(node1, state, cc, argAp, ap, config) and + PrevStage::readStepCand(node1, c, node2, config) and + getHeadContent(ap) = c + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, + Ap ap, Configuration config + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, state, outercc, argAp, ap, config) and + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutNotFromArg( + NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + ) { + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, argAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + ccOut = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + ) + } + + pragma[nomagic] + private predicate storeStepFwd( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config + ) { + fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + ap2 = apCons(tc, ap1) and + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + } + + private predicate readStepFwd( + NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config + ) { + fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowConsCand(ap1, c, ap2, config) + } + + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and + fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), + pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + + pragma[nomagic] + private predicate returnNodeMayFlowThrough( + RetNodeEx ret, FlowState state, Ap ap, Configuration config + ) { + fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from the + * enclosing callable in order to reach a sink, and if so, `returnAp` records + * the access path of the returned value. + */ + pragma[nomagic] + predicate revFlow( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + revFlow0(node, state, toReturn, returnAp, ap, config) and + fwdFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + fwdFlow(node, state, _, _, ap, config) and + sinkNode(node, state, config) and + (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid, FlowState state0 | + localStep(node, state, mid, state0, true, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, ap, config) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, state, _, _, ap, config) and + toReturn = false and + returnAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStep(node, mid, config) and + revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStateStep(node, state, mid, state0, config) and + revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, + pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowConsCand(ap0, c, ap, config) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, state, toReturn, returnAp, ap0, config) and + readStepFwd(node, ap, _, mid, ap0, config) + ) + or + // flow into a callable + revFlowInNotToReturn(node, state, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + or + // flow out of a callable + revFlowOut(_, node, state, _, _, ap, config) and + toReturn = true and + if returnNodeMayFlowThrough(node, state, ap, config) + then returnAp = apSome(ap) + else returnAp = apNone() + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, + boolean toReturn, ApOption returnAp, Configuration config + ) { + revFlow(mid, state, toReturn, returnAp, ap0, config) and + storeStepFwd(node, ap, tc, mid, ap0, config) and + tc.getContent() = c + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, _, tail, config) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0, config) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, + Configuration config + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, state, toReturn, returnAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInNotToReturn( + ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, FlowState state, CcCall ccc | + revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and + fwdFlow(ret, state, ccc, apSome(_), ap, config) and + matchesCall(ccc, call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Ap ap2, Content c | + PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and + revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and + revFlowConsCand(ap2, c, ap1, config) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and + readStepFwd(node1, ap1, c, node2, ap2, config) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, + pragma[only_bind_into](config)) + ) + } + + predicate revFlow(NodeEx node, FlowState state, Configuration config) { + revFlow(node, state, _, _, _, config) + } + + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, ap, config) + } + + private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepFwd(_, ap, tc, _, _, config) + } + + private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepCand(_, ap, tc, _, _, config) + } + + private predicate validAp(Ap ap, Configuration config) { + revFlow(_, _, _, _, ap, config) and ap instanceof ApNil + or + exists(TypedContent head, Ap tail | + consCand(head, tail, config) and + ap = apCons(head, tail) + ) + } + + predicate consCand(TypedContent tc, Ap ap, Configuration config) { + revConsCand(tc, ap, config) and + validAp(ap, config) + } + + pragma[noinline] + private predicate parameterFlow( + ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ) { + revFlow(p, _, true, apSome(ap0), ap, config) and + c = p.getEnclosingCallable() + } + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | + parameterFlow(p, ap, ap0, c, config) and + c = ret.getEnclosingCallable() and + revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), + pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and + fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and + kind = ret.getKind() and + p.getPosition() = pos and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists( + Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + | + revFlow(arg, state, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + + predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, argAp, ap, config) + ) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | consCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and + states = count(FlowState state | revFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | + revFlow(n, state, b, retAp, ap, config) + ) + } + /* End: Stage logic. */ + } +} + +private module BooleanCallContext { + class Cc extends boolean { + Cc() { this in [true, false] } + } + + class CcCall extends Cc { + CcCall() { this = true } + } + + /** Holds if the call context may be `call`. */ + predicate matchesCall(CcCall cc, DataFlowCall call) { any() } + + class CcNoCall extends Cc { + CcNoCall() { this = false } + } + + Cc ccNone() { result = false } + + CcCall ccSomeCall() { result = true } + + class LocalCc = Unit; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } +} + +private module Level1CallContext { class Cc = CallContext; class CcCall = CallContextCall; + pragma[inline] + predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } + class CcNoCall = CallContextNoCall; Cc ccNone() { result instanceof CallContextAny } CcCall ccSomeCall() { result instanceof CallContextSomeCall } - private class LocalCc = Unit; + module NoLocalCallContext { + class LocalCc = Unit; - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSiteDispatch(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSiteDispatch(call, c) + then result = TSpecificCall(call) + else result = TSomeCall() + } + } + + module LocalCallContext { + class LocalCc = LocalCallContext; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { + result = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + node.getEnclosingCallable()) + } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() + } } bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { checkCallContextReturn(innercc, c, call) and if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() } +} - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } +private module Stage2Param implements MkStage::StageParam { + private module PrevStage = Stage1; + + class Ap extends boolean { + Ap() { this in [true, false] } + } + + class ApNil extends Ap { + ApNil() { this = false } + } + + bindingset[result, ap] + PrevStage::Ap getApprox(Ap ap) { any() } + + ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + + pragma[inline] + Content getHeadContent(Ap ap) { exists(result) and ap = true } + + class ApOption = BooleanOption; + + ApOption apNone() { result = TBooleanNone() } + + ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + + import Level1CallContext + import NoLocalCallContext bindingset[node1, state1, config] bindingset[node2, state2, config] - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -1221,9 +1906,9 @@ private module Stage2 { exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/5; - private predicate flowIntoCall = flowIntoCallNodeCand1/5; + predicate flowIntoCall = flowIntoCallNodeCand1/5; pragma[nomagic] private predicate expectsContentCand(NodeEx node, Configuration config) { @@ -1235,7 +1920,7 @@ private module Stage2 { } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and not stateBarrier(node, state, config) and @@ -1248,544 +1933,11 @@ private module Stage2 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 2 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 2 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage2 = MkStage::Stage; + pragma[nomagic] private predicate flowOutOfCallNodeCand2( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config @@ -1883,14 +2035,13 @@ private module LocalFlowBigStep { ) { additionalLocalFlowStepNodeCand1(node1, node2, config) and state1 = state2 and - Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false, - pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false, + Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false, pragma[only_bind_into](config)) or additionalLocalStateStep(node1, state1, node2, state2, config) and - Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config)) + Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config)) } /** @@ -1967,26 +2118,24 @@ private module LocalFlowBigStep { private import LocalFlowBigStep -private module Stage3 { - module PrevStage = Stage2; - - class ApApprox = PrevStage::Ap; +private module Stage3Param implements MkStage::StageParam { + private module PrevStage = Stage2; class Ap = AccessPathFront; class ApNil = AccessPathFrontNil; - private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } + PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } + Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathFrontOption; @@ -1994,44 +2143,18 @@ private module Stage3 { ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - class Cc = boolean; + import BooleanCallContext - class CcCall extends Cc { - CcCall() { this = true } - - /** Holds if this call context may be `call`. */ - predicate matchesCall(DataFlowCall call) { any() } - } - - class CcNoCall extends Cc { - CcNoCall() { this = false } - } - - Cc ccNone() { result = false } - - CcCall ccSomeCall() { result = true } - - private class LocalCc = Unit; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/5; - private predicate flowIntoCall = flowIntoCallNodeCand2/5; + predicate flowIntoCall = flowIntoCallNodeCand2/5; pragma[nomagic] private predicate clearSet(NodeEx node, ContentSet c, Configuration config) { @@ -2067,7 +2190,7 @@ private module Stage3 { private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { exists(state) and exists(config) and not clear(node, ap, config) and @@ -2080,548 +2203,15 @@ private module Stage3 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { + predicate typecheckStore(Ap ap, DataFlowType contentType) { // We need to typecheck stores here, since reverse flow through a getter // might have a different type here compared to inside the getter. compatibleTypes(ap.getType(), contentType) } - - /* Begin: Stage 3 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 3 logic. */ } +private module Stage3 = MkStage::Stage; + /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` * and remains relevant for the following pruning stage. @@ -2644,7 +2234,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and nodes = strictcount(NodeEx n, FlowState state | - Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) or flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) ) and @@ -2828,26 +2418,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } } -private module Stage4 { - module PrevStage = Stage3; - - class ApApprox = PrevStage::Ap; +private module Stage4Param implements MkStage::StageParam { + private module PrevStage = Stage3; class Ap = AccessPathApprox; class ApNil = AccessPathApproxNil; - private ApApprox getApprox(Ap ap) { result = ap.getFront() } + PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } + Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathApproxOption; @@ -2855,38 +2443,10 @@ private module Stage4 { ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - class Cc = CallContext; + import Level1CallContext + import LocalCallContext - class CcCall = CallContextCall; - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - private class LocalCc = LocalCallContext; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() - } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { - result = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - node.getEnclosingCallable()) - } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -2894,575 +2454,40 @@ private module Stage4 { } pragma[nomagic] - private predicate flowOutOfCall( + predicate flowOutOfCall( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } pragma[nomagic] - private predicate flowIntoCall( + predicate flowIntoCall( DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } // Type checking is not necessary here as it has already been done in stage 3. bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 4 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 4 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage4 = MkStage::Stage; + bindingset[conf, result] private Configuration unbindConf(Configuration conf) { exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) @@ -3495,7 +2520,7 @@ private newtype TSummaryCtx = TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and - Stage4::revFlow(p, state, _, _, _, config) + Stage4::revFlow(p, state, _, config) ) } @@ -3553,7 +2578,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { result = strictcount(NodeEx n, FlowState state | - Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config) + Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config) ) } @@ -3667,7 +2692,7 @@ private newtype TPathNode = exists(PathNodeMid mid | pathStep(mid, node, state, cc, sc, ap) and pragma[only_bind_into](config) = mid.getConfiguration() and - Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config)) + Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config)) ) } or TPathNodeSink(NodeEx node, FlowState state, Configuration config) { @@ -4207,7 +3232,7 @@ private NodeEx getAnOutNodeFlow( ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result.asNode() = kind.getAnOutNode(call) and - Stage4::revFlow(result, _, _, _, apa, config) + Stage4::revFlow(result, _, apa, config) } /** @@ -4243,7 +3268,7 @@ private predicate parameterCand( DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config ) { exists(ParamNodeEx p | - Stage4::revFlow(p, _, _, _, apa, config) and + Stage4::revFlow(p, _, apa, config) and p.isParameterOf(callable, pos) ) } diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll index a076f7a2e45..22c3bd2466e 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,12 +944,9 @@ private module Stage1 { predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) } bindingset[node, state, config] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, toReturn, pragma[only_bind_into](config)) and + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, _, pragma[only_bind_into](config)) and exists(state) and - exists(returnAp) and exists(ap) } @@ -1142,66 +1139,754 @@ private predicate flowIntoCallNodeCand1( ) } -private module Stage2 { - module PrevStage = Stage1; +private signature module StageSig { + class Ap; + predicate revFlow(NodeEx node, Configuration config); + + bindingset[node, state, config] + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config); + + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ); + + predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config); +} + +private module MkStage { class ApApprox = PrevStage::Ap; - class Ap = boolean; + signature module StageParam { + class Ap; - class ApNil extends Ap { - ApNil() { this = false } + class ApNil extends Ap; + + bindingset[result, ap] + ApApprox getApprox(Ap ap); + + ApNil getApNil(NodeEx node); + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail); + + Content getHeadContent(Ap ap); + + class ApOption; + + ApOption apNone(); + + ApOption apSome(Ap ap); + + class Cc; + + class CcCall extends Cc; + + // TODO: member predicate on CcCall + predicate matchesCall(CcCall cc, DataFlowCall call); + + class CcNoCall extends Cc; + + Cc ccNone(); + + CcCall ccSomeCall(); + + class LocalCc; + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc); + + bindingset[node1, state1, config] + bindingset[node2, state2, config] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ); + + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + ); + + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ); + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config); + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType); } - bindingset[result, ap] - private ApApprox getApprox(Ap ap) { any() } + module Stage implements StageSig { + import Param - private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) } + /* Begin: Stage logic. */ + bindingset[result, apa] + private ApApprox unbindApa(ApApprox apa) { + pragma[only_bind_out](apa) = pragma[only_bind_out](result) + } - bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, + Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, + pragma[only_bind_into](config)) and + matchesCall(ccc, call) + } - pragma[inline] - private Content getHeadContent(Ap ap) { exists(result) and ap = true } + /** + * Holds if `node` is reachable with access path `ap` from a source in the + * configuration `config`. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. + */ + pragma[nomagic] + predicate fwdFlow( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + fwdFlow0(node, state, cc, argAp, ap, config) and + PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and + filter(node, state, ap, config) + } - class ApOption = BooleanOption; + pragma[nomagic] + private predicate fwdFlow0( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + sourceNode(node, state, config) and + (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and + argAp = apNone() and + ap = getApNil(node) + or + exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | + fwdFlow(mid, state0, cc, argAp, ap0, config) and + localCc = getLocalCc(mid, cc) + | + localStep(mid, state0, node, state, true, _, config, localCc) and + ap = ap0 + or + localStep(mid, state0, node, state, false, ap, config, localCc) and + ap0 instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + jumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStateStep(mid, state0, node, state, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + // store + exists(TypedContent tc, Ap ap0 | + fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + ap = apCons(tc, ap0) + ) + or + // read + exists(Ap ap0, Content c | + fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowConsCand(ap0, c, ap, config) + ) + or + // flow into a callable + exists(ApApprox apa | + fwdFlowIn(_, node, state, _, cc, _, ap, config) and + apa = getApprox(ap) and + if PrevStage::parameterMayFlowThrough(node, _, apa, config) + then argAp = apSome(ap) + else argAp = apNone() + ) + or + // flow out of a callable + fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) + ) + } - ApOption apNone() { result = TBooleanNone() } + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + exists(DataFlowType contentType | + fwdFlow(node1, state, cc, argAp, ap1, config) and + PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and + typecheckStore(ap1, contentType) + ) + } - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + /** + * Holds if forward flow with access path `tail` reaches a store of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(TypedContent tc | + fwdFlowStore(_, tail, tc, _, _, _, _, config) and + tc.getContent() = c and + cons = apCons(tc, tail) + ) + } + pragma[nomagic] + private predicate fwdFlowRead( + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + fwdFlow(node1, state, cc, argAp, ap, config) and + PrevStage::readStepCand(node1, c, node2, config) and + getHeadContent(ap) = c + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, + Ap ap, Configuration config + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, state, outercc, argAp, ap, config) and + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutNotFromArg( + NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + ) { + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, argAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + ccOut = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + ) + } + + pragma[nomagic] + private predicate storeStepFwd( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config + ) { + fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + ap2 = apCons(tc, ap1) and + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + } + + private predicate readStepFwd( + NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config + ) { + fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowConsCand(ap1, c, ap2, config) + } + + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and + fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), + pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + + pragma[nomagic] + private predicate returnNodeMayFlowThrough( + RetNodeEx ret, FlowState state, Ap ap, Configuration config + ) { + fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from the + * enclosing callable in order to reach a sink, and if so, `returnAp` records + * the access path of the returned value. + */ + pragma[nomagic] + predicate revFlow( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + revFlow0(node, state, toReturn, returnAp, ap, config) and + fwdFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + fwdFlow(node, state, _, _, ap, config) and + sinkNode(node, state, config) and + (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid, FlowState state0 | + localStep(node, state, mid, state0, true, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, ap, config) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, state, _, _, ap, config) and + toReturn = false and + returnAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStep(node, mid, config) and + revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStateStep(node, state, mid, state0, config) and + revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, + pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowConsCand(ap0, c, ap, config) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, state, toReturn, returnAp, ap0, config) and + readStepFwd(node, ap, _, mid, ap0, config) + ) + or + // flow into a callable + revFlowInNotToReturn(node, state, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + or + // flow out of a callable + revFlowOut(_, node, state, _, _, ap, config) and + toReturn = true and + if returnNodeMayFlowThrough(node, state, ap, config) + then returnAp = apSome(ap) + else returnAp = apNone() + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, + boolean toReturn, ApOption returnAp, Configuration config + ) { + revFlow(mid, state, toReturn, returnAp, ap0, config) and + storeStepFwd(node, ap, tc, mid, ap0, config) and + tc.getContent() = c + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, _, tail, config) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0, config) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, + Configuration config + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, state, toReturn, returnAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInNotToReturn( + ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, FlowState state, CcCall ccc | + revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and + fwdFlow(ret, state, ccc, apSome(_), ap, config) and + matchesCall(ccc, call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Ap ap2, Content c | + PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and + revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and + revFlowConsCand(ap2, c, ap1, config) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and + readStepFwd(node1, ap1, c, node2, ap2, config) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, + pragma[only_bind_into](config)) + ) + } + + predicate revFlow(NodeEx node, FlowState state, Configuration config) { + revFlow(node, state, _, _, _, config) + } + + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, ap, config) + } + + private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepFwd(_, ap, tc, _, _, config) + } + + private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepCand(_, ap, tc, _, _, config) + } + + private predicate validAp(Ap ap, Configuration config) { + revFlow(_, _, _, _, ap, config) and ap instanceof ApNil + or + exists(TypedContent head, Ap tail | + consCand(head, tail, config) and + ap = apCons(head, tail) + ) + } + + predicate consCand(TypedContent tc, Ap ap, Configuration config) { + revConsCand(tc, ap, config) and + validAp(ap, config) + } + + pragma[noinline] + private predicate parameterFlow( + ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ) { + revFlow(p, _, true, apSome(ap0), ap, config) and + c = p.getEnclosingCallable() + } + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | + parameterFlow(p, ap, ap0, c, config) and + c = ret.getEnclosingCallable() and + revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), + pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and + fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and + kind = ret.getKind() and + p.getPosition() = pos and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists( + Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + | + revFlow(arg, state, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + + predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, argAp, ap, config) + ) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | consCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and + states = count(FlowState state | revFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | + revFlow(n, state, b, retAp, ap, config) + ) + } + /* End: Stage logic. */ + } +} + +private module BooleanCallContext { + class Cc extends boolean { + Cc() { this in [true, false] } + } + + class CcCall extends Cc { + CcCall() { this = true } + } + + /** Holds if the call context may be `call`. */ + predicate matchesCall(CcCall cc, DataFlowCall call) { any() } + + class CcNoCall extends Cc { + CcNoCall() { this = false } + } + + Cc ccNone() { result = false } + + CcCall ccSomeCall() { result = true } + + class LocalCc = Unit; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } +} + +private module Level1CallContext { class Cc = CallContext; class CcCall = CallContextCall; + pragma[inline] + predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } + class CcNoCall = CallContextNoCall; Cc ccNone() { result instanceof CallContextAny } CcCall ccSomeCall() { result instanceof CallContextSomeCall } - private class LocalCc = Unit; + module NoLocalCallContext { + class LocalCc = Unit; - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSiteDispatch(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSiteDispatch(call, c) + then result = TSpecificCall(call) + else result = TSomeCall() + } + } + + module LocalCallContext { + class LocalCc = LocalCallContext; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { + result = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + node.getEnclosingCallable()) + } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() + } } bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { checkCallContextReturn(innercc, c, call) and if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() } +} - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } +private module Stage2Param implements MkStage::StageParam { + private module PrevStage = Stage1; + + class Ap extends boolean { + Ap() { this in [true, false] } + } + + class ApNil extends Ap { + ApNil() { this = false } + } + + bindingset[result, ap] + PrevStage::Ap getApprox(Ap ap) { any() } + + ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + + pragma[inline] + Content getHeadContent(Ap ap) { exists(result) and ap = true } + + class ApOption = BooleanOption; + + ApOption apNone() { result = TBooleanNone() } + + ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + + import Level1CallContext + import NoLocalCallContext bindingset[node1, state1, config] bindingset[node2, state2, config] - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -1221,9 +1906,9 @@ private module Stage2 { exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/5; - private predicate flowIntoCall = flowIntoCallNodeCand1/5; + predicate flowIntoCall = flowIntoCallNodeCand1/5; pragma[nomagic] private predicate expectsContentCand(NodeEx node, Configuration config) { @@ -1235,7 +1920,7 @@ private module Stage2 { } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and not stateBarrier(node, state, config) and @@ -1248,544 +1933,11 @@ private module Stage2 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 2 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 2 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage2 = MkStage::Stage; + pragma[nomagic] private predicate flowOutOfCallNodeCand2( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config @@ -1883,14 +2035,13 @@ private module LocalFlowBigStep { ) { additionalLocalFlowStepNodeCand1(node1, node2, config) and state1 = state2 and - Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false, - pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false, + Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false, pragma[only_bind_into](config)) or additionalLocalStateStep(node1, state1, node2, state2, config) and - Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config)) + Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config)) } /** @@ -1967,26 +2118,24 @@ private module LocalFlowBigStep { private import LocalFlowBigStep -private module Stage3 { - module PrevStage = Stage2; - - class ApApprox = PrevStage::Ap; +private module Stage3Param implements MkStage::StageParam { + private module PrevStage = Stage2; class Ap = AccessPathFront; class ApNil = AccessPathFrontNil; - private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } + PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } + Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathFrontOption; @@ -1994,44 +2143,18 @@ private module Stage3 { ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - class Cc = boolean; + import BooleanCallContext - class CcCall extends Cc { - CcCall() { this = true } - - /** Holds if this call context may be `call`. */ - predicate matchesCall(DataFlowCall call) { any() } - } - - class CcNoCall extends Cc { - CcNoCall() { this = false } - } - - Cc ccNone() { result = false } - - CcCall ccSomeCall() { result = true } - - private class LocalCc = Unit; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/5; - private predicate flowIntoCall = flowIntoCallNodeCand2/5; + predicate flowIntoCall = flowIntoCallNodeCand2/5; pragma[nomagic] private predicate clearSet(NodeEx node, ContentSet c, Configuration config) { @@ -2067,7 +2190,7 @@ private module Stage3 { private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { exists(state) and exists(config) and not clear(node, ap, config) and @@ -2080,548 +2203,15 @@ private module Stage3 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { + predicate typecheckStore(Ap ap, DataFlowType contentType) { // We need to typecheck stores here, since reverse flow through a getter // might have a different type here compared to inside the getter. compatibleTypes(ap.getType(), contentType) } - - /* Begin: Stage 3 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 3 logic. */ } +private module Stage3 = MkStage::Stage; + /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` * and remains relevant for the following pruning stage. @@ -2644,7 +2234,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and nodes = strictcount(NodeEx n, FlowState state | - Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) or flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) ) and @@ -2828,26 +2418,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } } -private module Stage4 { - module PrevStage = Stage3; - - class ApApprox = PrevStage::Ap; +private module Stage4Param implements MkStage::StageParam { + private module PrevStage = Stage3; class Ap = AccessPathApprox; class ApNil = AccessPathApproxNil; - private ApApprox getApprox(Ap ap) { result = ap.getFront() } + PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } + Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathApproxOption; @@ -2855,38 +2443,10 @@ private module Stage4 { ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - class Cc = CallContext; + import Level1CallContext + import LocalCallContext - class CcCall = CallContextCall; - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - private class LocalCc = LocalCallContext; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() - } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { - result = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - node.getEnclosingCallable()) - } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -2894,575 +2454,40 @@ private module Stage4 { } pragma[nomagic] - private predicate flowOutOfCall( + predicate flowOutOfCall( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } pragma[nomagic] - private predicate flowIntoCall( + predicate flowIntoCall( DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } // Type checking is not necessary here as it has already been done in stage 3. bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 4 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 4 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage4 = MkStage::Stage; + bindingset[conf, result] private Configuration unbindConf(Configuration conf) { exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) @@ -3495,7 +2520,7 @@ private newtype TSummaryCtx = TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and - Stage4::revFlow(p, state, _, _, _, config) + Stage4::revFlow(p, state, _, config) ) } @@ -3553,7 +2578,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { result = strictcount(NodeEx n, FlowState state | - Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config) + Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config) ) } @@ -3667,7 +2692,7 @@ private newtype TPathNode = exists(PathNodeMid mid | pathStep(mid, node, state, cc, sc, ap) and pragma[only_bind_into](config) = mid.getConfiguration() and - Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config)) + Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config)) ) } or TPathNodeSink(NodeEx node, FlowState state, Configuration config) { @@ -4207,7 +3232,7 @@ private NodeEx getAnOutNodeFlow( ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result.asNode() = kind.getAnOutNode(call) and - Stage4::revFlow(result, _, _, _, apa, config) + Stage4::revFlow(result, _, apa, config) } /** @@ -4243,7 +3268,7 @@ private predicate parameterCand( DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config ) { exists(ParamNodeEx p | - Stage4::revFlow(p, _, _, _, apa, config) and + Stage4::revFlow(p, _, apa, config) and p.isParameterOf(callable, pos) ) } diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll index a076f7a2e45..22c3bd2466e 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,12 +944,9 @@ private module Stage1 { predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) } bindingset[node, state, config] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, toReturn, pragma[only_bind_into](config)) and + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, _, pragma[only_bind_into](config)) and exists(state) and - exists(returnAp) and exists(ap) } @@ -1142,66 +1139,754 @@ private predicate flowIntoCallNodeCand1( ) } -private module Stage2 { - module PrevStage = Stage1; +private signature module StageSig { + class Ap; + predicate revFlow(NodeEx node, Configuration config); + + bindingset[node, state, config] + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config); + + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ); + + predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config); +} + +private module MkStage { class ApApprox = PrevStage::Ap; - class Ap = boolean; + signature module StageParam { + class Ap; - class ApNil extends Ap { - ApNil() { this = false } + class ApNil extends Ap; + + bindingset[result, ap] + ApApprox getApprox(Ap ap); + + ApNil getApNil(NodeEx node); + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail); + + Content getHeadContent(Ap ap); + + class ApOption; + + ApOption apNone(); + + ApOption apSome(Ap ap); + + class Cc; + + class CcCall extends Cc; + + // TODO: member predicate on CcCall + predicate matchesCall(CcCall cc, DataFlowCall call); + + class CcNoCall extends Cc; + + Cc ccNone(); + + CcCall ccSomeCall(); + + class LocalCc; + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc); + + bindingset[node1, state1, config] + bindingset[node2, state2, config] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ); + + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + ); + + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ); + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config); + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType); } - bindingset[result, ap] - private ApApprox getApprox(Ap ap) { any() } + module Stage implements StageSig { + import Param - private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) } + /* Begin: Stage logic. */ + bindingset[result, apa] + private ApApprox unbindApa(ApApprox apa) { + pragma[only_bind_out](apa) = pragma[only_bind_out](result) + } - bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, + Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, + pragma[only_bind_into](config)) and + matchesCall(ccc, call) + } - pragma[inline] - private Content getHeadContent(Ap ap) { exists(result) and ap = true } + /** + * Holds if `node` is reachable with access path `ap` from a source in the + * configuration `config`. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. + */ + pragma[nomagic] + predicate fwdFlow( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + fwdFlow0(node, state, cc, argAp, ap, config) and + PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and + filter(node, state, ap, config) + } - class ApOption = BooleanOption; + pragma[nomagic] + private predicate fwdFlow0( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + sourceNode(node, state, config) and + (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and + argAp = apNone() and + ap = getApNil(node) + or + exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | + fwdFlow(mid, state0, cc, argAp, ap0, config) and + localCc = getLocalCc(mid, cc) + | + localStep(mid, state0, node, state, true, _, config, localCc) and + ap = ap0 + or + localStep(mid, state0, node, state, false, ap, config, localCc) and + ap0 instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + jumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStateStep(mid, state0, node, state, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + // store + exists(TypedContent tc, Ap ap0 | + fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + ap = apCons(tc, ap0) + ) + or + // read + exists(Ap ap0, Content c | + fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowConsCand(ap0, c, ap, config) + ) + or + // flow into a callable + exists(ApApprox apa | + fwdFlowIn(_, node, state, _, cc, _, ap, config) and + apa = getApprox(ap) and + if PrevStage::parameterMayFlowThrough(node, _, apa, config) + then argAp = apSome(ap) + else argAp = apNone() + ) + or + // flow out of a callable + fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) + ) + } - ApOption apNone() { result = TBooleanNone() } + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + exists(DataFlowType contentType | + fwdFlow(node1, state, cc, argAp, ap1, config) and + PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and + typecheckStore(ap1, contentType) + ) + } - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + /** + * Holds if forward flow with access path `tail` reaches a store of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(TypedContent tc | + fwdFlowStore(_, tail, tc, _, _, _, _, config) and + tc.getContent() = c and + cons = apCons(tc, tail) + ) + } + pragma[nomagic] + private predicate fwdFlowRead( + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + fwdFlow(node1, state, cc, argAp, ap, config) and + PrevStage::readStepCand(node1, c, node2, config) and + getHeadContent(ap) = c + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, + Ap ap, Configuration config + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, state, outercc, argAp, ap, config) and + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutNotFromArg( + NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + ) { + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, argAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + ccOut = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + ) + } + + pragma[nomagic] + private predicate storeStepFwd( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config + ) { + fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + ap2 = apCons(tc, ap1) and + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + } + + private predicate readStepFwd( + NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config + ) { + fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowConsCand(ap1, c, ap2, config) + } + + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and + fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), + pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + + pragma[nomagic] + private predicate returnNodeMayFlowThrough( + RetNodeEx ret, FlowState state, Ap ap, Configuration config + ) { + fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from the + * enclosing callable in order to reach a sink, and if so, `returnAp` records + * the access path of the returned value. + */ + pragma[nomagic] + predicate revFlow( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + revFlow0(node, state, toReturn, returnAp, ap, config) and + fwdFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + fwdFlow(node, state, _, _, ap, config) and + sinkNode(node, state, config) and + (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid, FlowState state0 | + localStep(node, state, mid, state0, true, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, ap, config) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, state, _, _, ap, config) and + toReturn = false and + returnAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStep(node, mid, config) and + revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStateStep(node, state, mid, state0, config) and + revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, + pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowConsCand(ap0, c, ap, config) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, state, toReturn, returnAp, ap0, config) and + readStepFwd(node, ap, _, mid, ap0, config) + ) + or + // flow into a callable + revFlowInNotToReturn(node, state, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + or + // flow out of a callable + revFlowOut(_, node, state, _, _, ap, config) and + toReturn = true and + if returnNodeMayFlowThrough(node, state, ap, config) + then returnAp = apSome(ap) + else returnAp = apNone() + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, + boolean toReturn, ApOption returnAp, Configuration config + ) { + revFlow(mid, state, toReturn, returnAp, ap0, config) and + storeStepFwd(node, ap, tc, mid, ap0, config) and + tc.getContent() = c + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, _, tail, config) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0, config) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, + Configuration config + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, state, toReturn, returnAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInNotToReturn( + ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, FlowState state, CcCall ccc | + revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and + fwdFlow(ret, state, ccc, apSome(_), ap, config) and + matchesCall(ccc, call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Ap ap2, Content c | + PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and + revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and + revFlowConsCand(ap2, c, ap1, config) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and + readStepFwd(node1, ap1, c, node2, ap2, config) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, + pragma[only_bind_into](config)) + ) + } + + predicate revFlow(NodeEx node, FlowState state, Configuration config) { + revFlow(node, state, _, _, _, config) + } + + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, ap, config) + } + + private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepFwd(_, ap, tc, _, _, config) + } + + private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepCand(_, ap, tc, _, _, config) + } + + private predicate validAp(Ap ap, Configuration config) { + revFlow(_, _, _, _, ap, config) and ap instanceof ApNil + or + exists(TypedContent head, Ap tail | + consCand(head, tail, config) and + ap = apCons(head, tail) + ) + } + + predicate consCand(TypedContent tc, Ap ap, Configuration config) { + revConsCand(tc, ap, config) and + validAp(ap, config) + } + + pragma[noinline] + private predicate parameterFlow( + ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ) { + revFlow(p, _, true, apSome(ap0), ap, config) and + c = p.getEnclosingCallable() + } + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | + parameterFlow(p, ap, ap0, c, config) and + c = ret.getEnclosingCallable() and + revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), + pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and + fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and + kind = ret.getKind() and + p.getPosition() = pos and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists( + Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + | + revFlow(arg, state, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + + predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, argAp, ap, config) + ) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | consCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and + states = count(FlowState state | revFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | + revFlow(n, state, b, retAp, ap, config) + ) + } + /* End: Stage logic. */ + } +} + +private module BooleanCallContext { + class Cc extends boolean { + Cc() { this in [true, false] } + } + + class CcCall extends Cc { + CcCall() { this = true } + } + + /** Holds if the call context may be `call`. */ + predicate matchesCall(CcCall cc, DataFlowCall call) { any() } + + class CcNoCall extends Cc { + CcNoCall() { this = false } + } + + Cc ccNone() { result = false } + + CcCall ccSomeCall() { result = true } + + class LocalCc = Unit; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } +} + +private module Level1CallContext { class Cc = CallContext; class CcCall = CallContextCall; + pragma[inline] + predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } + class CcNoCall = CallContextNoCall; Cc ccNone() { result instanceof CallContextAny } CcCall ccSomeCall() { result instanceof CallContextSomeCall } - private class LocalCc = Unit; + module NoLocalCallContext { + class LocalCc = Unit; - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSiteDispatch(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSiteDispatch(call, c) + then result = TSpecificCall(call) + else result = TSomeCall() + } + } + + module LocalCallContext { + class LocalCc = LocalCallContext; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { + result = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + node.getEnclosingCallable()) + } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() + } } bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { checkCallContextReturn(innercc, c, call) and if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() } +} - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } +private module Stage2Param implements MkStage::StageParam { + private module PrevStage = Stage1; + + class Ap extends boolean { + Ap() { this in [true, false] } + } + + class ApNil extends Ap { + ApNil() { this = false } + } + + bindingset[result, ap] + PrevStage::Ap getApprox(Ap ap) { any() } + + ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + + pragma[inline] + Content getHeadContent(Ap ap) { exists(result) and ap = true } + + class ApOption = BooleanOption; + + ApOption apNone() { result = TBooleanNone() } + + ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + + import Level1CallContext + import NoLocalCallContext bindingset[node1, state1, config] bindingset[node2, state2, config] - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -1221,9 +1906,9 @@ private module Stage2 { exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/5; - private predicate flowIntoCall = flowIntoCallNodeCand1/5; + predicate flowIntoCall = flowIntoCallNodeCand1/5; pragma[nomagic] private predicate expectsContentCand(NodeEx node, Configuration config) { @@ -1235,7 +1920,7 @@ private module Stage2 { } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and not stateBarrier(node, state, config) and @@ -1248,544 +1933,11 @@ private module Stage2 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 2 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 2 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage2 = MkStage::Stage; + pragma[nomagic] private predicate flowOutOfCallNodeCand2( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config @@ -1883,14 +2035,13 @@ private module LocalFlowBigStep { ) { additionalLocalFlowStepNodeCand1(node1, node2, config) and state1 = state2 and - Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false, - pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false, + Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false, pragma[only_bind_into](config)) or additionalLocalStateStep(node1, state1, node2, state2, config) and - Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config)) + Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config)) } /** @@ -1967,26 +2118,24 @@ private module LocalFlowBigStep { private import LocalFlowBigStep -private module Stage3 { - module PrevStage = Stage2; - - class ApApprox = PrevStage::Ap; +private module Stage3Param implements MkStage::StageParam { + private module PrevStage = Stage2; class Ap = AccessPathFront; class ApNil = AccessPathFrontNil; - private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } + PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } + Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathFrontOption; @@ -1994,44 +2143,18 @@ private module Stage3 { ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - class Cc = boolean; + import BooleanCallContext - class CcCall extends Cc { - CcCall() { this = true } - - /** Holds if this call context may be `call`. */ - predicate matchesCall(DataFlowCall call) { any() } - } - - class CcNoCall extends Cc { - CcNoCall() { this = false } - } - - Cc ccNone() { result = false } - - CcCall ccSomeCall() { result = true } - - private class LocalCc = Unit; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/5; - private predicate flowIntoCall = flowIntoCallNodeCand2/5; + predicate flowIntoCall = flowIntoCallNodeCand2/5; pragma[nomagic] private predicate clearSet(NodeEx node, ContentSet c, Configuration config) { @@ -2067,7 +2190,7 @@ private module Stage3 { private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { exists(state) and exists(config) and not clear(node, ap, config) and @@ -2080,548 +2203,15 @@ private module Stage3 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { + predicate typecheckStore(Ap ap, DataFlowType contentType) { // We need to typecheck stores here, since reverse flow through a getter // might have a different type here compared to inside the getter. compatibleTypes(ap.getType(), contentType) } - - /* Begin: Stage 3 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 3 logic. */ } +private module Stage3 = MkStage::Stage; + /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` * and remains relevant for the following pruning stage. @@ -2644,7 +2234,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and nodes = strictcount(NodeEx n, FlowState state | - Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) or flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) ) and @@ -2828,26 +2418,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } } -private module Stage4 { - module PrevStage = Stage3; - - class ApApprox = PrevStage::Ap; +private module Stage4Param implements MkStage::StageParam { + private module PrevStage = Stage3; class Ap = AccessPathApprox; class ApNil = AccessPathApproxNil; - private ApApprox getApprox(Ap ap) { result = ap.getFront() } + PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } + Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathApproxOption; @@ -2855,38 +2443,10 @@ private module Stage4 { ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - class Cc = CallContext; + import Level1CallContext + import LocalCallContext - class CcCall = CallContextCall; - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - private class LocalCc = LocalCallContext; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() - } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { - result = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - node.getEnclosingCallable()) - } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -2894,575 +2454,40 @@ private module Stage4 { } pragma[nomagic] - private predicate flowOutOfCall( + predicate flowOutOfCall( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } pragma[nomagic] - private predicate flowIntoCall( + predicate flowIntoCall( DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } // Type checking is not necessary here as it has already been done in stage 3. bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 4 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 4 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage4 = MkStage::Stage; + bindingset[conf, result] private Configuration unbindConf(Configuration conf) { exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) @@ -3495,7 +2520,7 @@ private newtype TSummaryCtx = TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and - Stage4::revFlow(p, state, _, _, _, config) + Stage4::revFlow(p, state, _, config) ) } @@ -3553,7 +2578,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { result = strictcount(NodeEx n, FlowState state | - Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config) + Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config) ) } @@ -3667,7 +2692,7 @@ private newtype TPathNode = exists(PathNodeMid mid | pathStep(mid, node, state, cc, sc, ap) and pragma[only_bind_into](config) = mid.getConfiguration() and - Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config)) + Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config)) ) } or TPathNodeSink(NodeEx node, FlowState state, Configuration config) { @@ -4207,7 +3232,7 @@ private NodeEx getAnOutNodeFlow( ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result.asNode() = kind.getAnOutNode(call) and - Stage4::revFlow(result, _, _, _, apa, config) + Stage4::revFlow(result, _, apa, config) } /** @@ -4243,7 +3268,7 @@ private predicate parameterCand( DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config ) { exists(ParamNodeEx p | - Stage4::revFlow(p, _, _, _, apa, config) and + Stage4::revFlow(p, _, apa, config) and p.isParameterOf(callable, pos) ) } diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll index a076f7a2e45..22c3bd2466e 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,12 +944,9 @@ private module Stage1 { predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) } bindingset[node, state, config] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, toReturn, pragma[only_bind_into](config)) and + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, _, pragma[only_bind_into](config)) and exists(state) and - exists(returnAp) and exists(ap) } @@ -1142,66 +1139,754 @@ private predicate flowIntoCallNodeCand1( ) } -private module Stage2 { - module PrevStage = Stage1; +private signature module StageSig { + class Ap; + predicate revFlow(NodeEx node, Configuration config); + + bindingset[node, state, config] + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config); + + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ); + + predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config); +} + +private module MkStage { class ApApprox = PrevStage::Ap; - class Ap = boolean; + signature module StageParam { + class Ap; - class ApNil extends Ap { - ApNil() { this = false } + class ApNil extends Ap; + + bindingset[result, ap] + ApApprox getApprox(Ap ap); + + ApNil getApNil(NodeEx node); + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail); + + Content getHeadContent(Ap ap); + + class ApOption; + + ApOption apNone(); + + ApOption apSome(Ap ap); + + class Cc; + + class CcCall extends Cc; + + // TODO: member predicate on CcCall + predicate matchesCall(CcCall cc, DataFlowCall call); + + class CcNoCall extends Cc; + + Cc ccNone(); + + CcCall ccSomeCall(); + + class LocalCc; + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc); + + bindingset[node1, state1, config] + bindingset[node2, state2, config] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ); + + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + ); + + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ); + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config); + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType); } - bindingset[result, ap] - private ApApprox getApprox(Ap ap) { any() } + module Stage implements StageSig { + import Param - private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) } + /* Begin: Stage logic. */ + bindingset[result, apa] + private ApApprox unbindApa(ApApprox apa) { + pragma[only_bind_out](apa) = pragma[only_bind_out](result) + } - bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, + Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, + pragma[only_bind_into](config)) and + matchesCall(ccc, call) + } - pragma[inline] - private Content getHeadContent(Ap ap) { exists(result) and ap = true } + /** + * Holds if `node` is reachable with access path `ap` from a source in the + * configuration `config`. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. + */ + pragma[nomagic] + predicate fwdFlow( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + fwdFlow0(node, state, cc, argAp, ap, config) and + PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and + filter(node, state, ap, config) + } - class ApOption = BooleanOption; + pragma[nomagic] + private predicate fwdFlow0( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + sourceNode(node, state, config) and + (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and + argAp = apNone() and + ap = getApNil(node) + or + exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | + fwdFlow(mid, state0, cc, argAp, ap0, config) and + localCc = getLocalCc(mid, cc) + | + localStep(mid, state0, node, state, true, _, config, localCc) and + ap = ap0 + or + localStep(mid, state0, node, state, false, ap, config, localCc) and + ap0 instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + jumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStateStep(mid, state0, node, state, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + // store + exists(TypedContent tc, Ap ap0 | + fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + ap = apCons(tc, ap0) + ) + or + // read + exists(Ap ap0, Content c | + fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowConsCand(ap0, c, ap, config) + ) + or + // flow into a callable + exists(ApApprox apa | + fwdFlowIn(_, node, state, _, cc, _, ap, config) and + apa = getApprox(ap) and + if PrevStage::parameterMayFlowThrough(node, _, apa, config) + then argAp = apSome(ap) + else argAp = apNone() + ) + or + // flow out of a callable + fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) + ) + } - ApOption apNone() { result = TBooleanNone() } + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + exists(DataFlowType contentType | + fwdFlow(node1, state, cc, argAp, ap1, config) and + PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and + typecheckStore(ap1, contentType) + ) + } - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + /** + * Holds if forward flow with access path `tail` reaches a store of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(TypedContent tc | + fwdFlowStore(_, tail, tc, _, _, _, _, config) and + tc.getContent() = c and + cons = apCons(tc, tail) + ) + } + pragma[nomagic] + private predicate fwdFlowRead( + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + fwdFlow(node1, state, cc, argAp, ap, config) and + PrevStage::readStepCand(node1, c, node2, config) and + getHeadContent(ap) = c + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, + Ap ap, Configuration config + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, state, outercc, argAp, ap, config) and + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutNotFromArg( + NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + ) { + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, argAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + ccOut = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + ) + } + + pragma[nomagic] + private predicate storeStepFwd( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config + ) { + fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + ap2 = apCons(tc, ap1) and + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + } + + private predicate readStepFwd( + NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config + ) { + fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowConsCand(ap1, c, ap2, config) + } + + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and + fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), + pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + + pragma[nomagic] + private predicate returnNodeMayFlowThrough( + RetNodeEx ret, FlowState state, Ap ap, Configuration config + ) { + fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from the + * enclosing callable in order to reach a sink, and if so, `returnAp` records + * the access path of the returned value. + */ + pragma[nomagic] + predicate revFlow( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + revFlow0(node, state, toReturn, returnAp, ap, config) and + fwdFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + fwdFlow(node, state, _, _, ap, config) and + sinkNode(node, state, config) and + (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid, FlowState state0 | + localStep(node, state, mid, state0, true, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, ap, config) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, state, _, _, ap, config) and + toReturn = false and + returnAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStep(node, mid, config) and + revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStateStep(node, state, mid, state0, config) and + revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, + pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowConsCand(ap0, c, ap, config) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, state, toReturn, returnAp, ap0, config) and + readStepFwd(node, ap, _, mid, ap0, config) + ) + or + // flow into a callable + revFlowInNotToReturn(node, state, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + or + // flow out of a callable + revFlowOut(_, node, state, _, _, ap, config) and + toReturn = true and + if returnNodeMayFlowThrough(node, state, ap, config) + then returnAp = apSome(ap) + else returnAp = apNone() + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, + boolean toReturn, ApOption returnAp, Configuration config + ) { + revFlow(mid, state, toReturn, returnAp, ap0, config) and + storeStepFwd(node, ap, tc, mid, ap0, config) and + tc.getContent() = c + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, _, tail, config) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0, config) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, + Configuration config + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, state, toReturn, returnAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInNotToReturn( + ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, FlowState state, CcCall ccc | + revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and + fwdFlow(ret, state, ccc, apSome(_), ap, config) and + matchesCall(ccc, call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Ap ap2, Content c | + PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and + revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and + revFlowConsCand(ap2, c, ap1, config) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and + readStepFwd(node1, ap1, c, node2, ap2, config) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, + pragma[only_bind_into](config)) + ) + } + + predicate revFlow(NodeEx node, FlowState state, Configuration config) { + revFlow(node, state, _, _, _, config) + } + + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, ap, config) + } + + private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepFwd(_, ap, tc, _, _, config) + } + + private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepCand(_, ap, tc, _, _, config) + } + + private predicate validAp(Ap ap, Configuration config) { + revFlow(_, _, _, _, ap, config) and ap instanceof ApNil + or + exists(TypedContent head, Ap tail | + consCand(head, tail, config) and + ap = apCons(head, tail) + ) + } + + predicate consCand(TypedContent tc, Ap ap, Configuration config) { + revConsCand(tc, ap, config) and + validAp(ap, config) + } + + pragma[noinline] + private predicate parameterFlow( + ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ) { + revFlow(p, _, true, apSome(ap0), ap, config) and + c = p.getEnclosingCallable() + } + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | + parameterFlow(p, ap, ap0, c, config) and + c = ret.getEnclosingCallable() and + revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), + pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and + fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and + kind = ret.getKind() and + p.getPosition() = pos and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists( + Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + | + revFlow(arg, state, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + + predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, argAp, ap, config) + ) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | consCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and + states = count(FlowState state | revFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | + revFlow(n, state, b, retAp, ap, config) + ) + } + /* End: Stage logic. */ + } +} + +private module BooleanCallContext { + class Cc extends boolean { + Cc() { this in [true, false] } + } + + class CcCall extends Cc { + CcCall() { this = true } + } + + /** Holds if the call context may be `call`. */ + predicate matchesCall(CcCall cc, DataFlowCall call) { any() } + + class CcNoCall extends Cc { + CcNoCall() { this = false } + } + + Cc ccNone() { result = false } + + CcCall ccSomeCall() { result = true } + + class LocalCc = Unit; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } +} + +private module Level1CallContext { class Cc = CallContext; class CcCall = CallContextCall; + pragma[inline] + predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } + class CcNoCall = CallContextNoCall; Cc ccNone() { result instanceof CallContextAny } CcCall ccSomeCall() { result instanceof CallContextSomeCall } - private class LocalCc = Unit; + module NoLocalCallContext { + class LocalCc = Unit; - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSiteDispatch(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSiteDispatch(call, c) + then result = TSpecificCall(call) + else result = TSomeCall() + } + } + + module LocalCallContext { + class LocalCc = LocalCallContext; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { + result = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + node.getEnclosingCallable()) + } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() + } } bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { checkCallContextReturn(innercc, c, call) and if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() } +} - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } +private module Stage2Param implements MkStage::StageParam { + private module PrevStage = Stage1; + + class Ap extends boolean { + Ap() { this in [true, false] } + } + + class ApNil extends Ap { + ApNil() { this = false } + } + + bindingset[result, ap] + PrevStage::Ap getApprox(Ap ap) { any() } + + ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + + pragma[inline] + Content getHeadContent(Ap ap) { exists(result) and ap = true } + + class ApOption = BooleanOption; + + ApOption apNone() { result = TBooleanNone() } + + ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + + import Level1CallContext + import NoLocalCallContext bindingset[node1, state1, config] bindingset[node2, state2, config] - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -1221,9 +1906,9 @@ private module Stage2 { exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/5; - private predicate flowIntoCall = flowIntoCallNodeCand1/5; + predicate flowIntoCall = flowIntoCallNodeCand1/5; pragma[nomagic] private predicate expectsContentCand(NodeEx node, Configuration config) { @@ -1235,7 +1920,7 @@ private module Stage2 { } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and not stateBarrier(node, state, config) and @@ -1248,544 +1933,11 @@ private module Stage2 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 2 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 2 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage2 = MkStage::Stage; + pragma[nomagic] private predicate flowOutOfCallNodeCand2( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config @@ -1883,14 +2035,13 @@ private module LocalFlowBigStep { ) { additionalLocalFlowStepNodeCand1(node1, node2, config) and state1 = state2 and - Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false, - pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false, + Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false, pragma[only_bind_into](config)) or additionalLocalStateStep(node1, state1, node2, state2, config) and - Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config)) + Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config)) } /** @@ -1967,26 +2118,24 @@ private module LocalFlowBigStep { private import LocalFlowBigStep -private module Stage3 { - module PrevStage = Stage2; - - class ApApprox = PrevStage::Ap; +private module Stage3Param implements MkStage::StageParam { + private module PrevStage = Stage2; class Ap = AccessPathFront; class ApNil = AccessPathFrontNil; - private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } + PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } + Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathFrontOption; @@ -1994,44 +2143,18 @@ private module Stage3 { ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - class Cc = boolean; + import BooleanCallContext - class CcCall extends Cc { - CcCall() { this = true } - - /** Holds if this call context may be `call`. */ - predicate matchesCall(DataFlowCall call) { any() } - } - - class CcNoCall extends Cc { - CcNoCall() { this = false } - } - - Cc ccNone() { result = false } - - CcCall ccSomeCall() { result = true } - - private class LocalCc = Unit; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/5; - private predicate flowIntoCall = flowIntoCallNodeCand2/5; + predicate flowIntoCall = flowIntoCallNodeCand2/5; pragma[nomagic] private predicate clearSet(NodeEx node, ContentSet c, Configuration config) { @@ -2067,7 +2190,7 @@ private module Stage3 { private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { exists(state) and exists(config) and not clear(node, ap, config) and @@ -2080,548 +2203,15 @@ private module Stage3 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { + predicate typecheckStore(Ap ap, DataFlowType contentType) { // We need to typecheck stores here, since reverse flow through a getter // might have a different type here compared to inside the getter. compatibleTypes(ap.getType(), contentType) } - - /* Begin: Stage 3 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 3 logic. */ } +private module Stage3 = MkStage::Stage; + /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` * and remains relevant for the following pruning stage. @@ -2644,7 +2234,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and nodes = strictcount(NodeEx n, FlowState state | - Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) or flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) ) and @@ -2828,26 +2418,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } } -private module Stage4 { - module PrevStage = Stage3; - - class ApApprox = PrevStage::Ap; +private module Stage4Param implements MkStage::StageParam { + private module PrevStage = Stage3; class Ap = AccessPathApprox; class ApNil = AccessPathApproxNil; - private ApApprox getApprox(Ap ap) { result = ap.getFront() } + PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } + Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathApproxOption; @@ -2855,38 +2443,10 @@ private module Stage4 { ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - class Cc = CallContext; + import Level1CallContext + import LocalCallContext - class CcCall = CallContextCall; - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - private class LocalCc = LocalCallContext; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() - } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { - result = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - node.getEnclosingCallable()) - } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -2894,575 +2454,40 @@ private module Stage4 { } pragma[nomagic] - private predicate flowOutOfCall( + predicate flowOutOfCall( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } pragma[nomagic] - private predicate flowIntoCall( + predicate flowIntoCall( DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } // Type checking is not necessary here as it has already been done in stage 3. bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 4 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 4 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage4 = MkStage::Stage; + bindingset[conf, result] private Configuration unbindConf(Configuration conf) { exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) @@ -3495,7 +2520,7 @@ private newtype TSummaryCtx = TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and - Stage4::revFlow(p, state, _, _, _, config) + Stage4::revFlow(p, state, _, config) ) } @@ -3553,7 +2578,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { result = strictcount(NodeEx n, FlowState state | - Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config) + Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config) ) } @@ -3667,7 +2692,7 @@ private newtype TPathNode = exists(PathNodeMid mid | pathStep(mid, node, state, cc, sc, ap) and pragma[only_bind_into](config) = mid.getConfiguration() and - Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config)) + Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config)) ) } or TPathNodeSink(NodeEx node, FlowState state, Configuration config) { @@ -4207,7 +3232,7 @@ private NodeEx getAnOutNodeFlow( ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result.asNode() = kind.getAnOutNode(call) and - Stage4::revFlow(result, _, _, _, apa, config) + Stage4::revFlow(result, _, apa, config) } /** @@ -4243,7 +3268,7 @@ private predicate parameterCand( DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config ) { exists(ParamNodeEx p | - Stage4::revFlow(p, _, _, _, apa, config) and + Stage4::revFlow(p, _, apa, config) and p.isParameterOf(callable, pos) ) } diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForLibraries.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForLibraries.qll index a076f7a2e45..22c3bd2466e 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForLibraries.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForLibraries.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,12 +944,9 @@ private module Stage1 { predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) } bindingset[node, state, config] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, toReturn, pragma[only_bind_into](config)) and + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, _, pragma[only_bind_into](config)) and exists(state) and - exists(returnAp) and exists(ap) } @@ -1142,66 +1139,754 @@ private predicate flowIntoCallNodeCand1( ) } -private module Stage2 { - module PrevStage = Stage1; +private signature module StageSig { + class Ap; + predicate revFlow(NodeEx node, Configuration config); + + bindingset[node, state, config] + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config); + + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ); + + predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config); +} + +private module MkStage { class ApApprox = PrevStage::Ap; - class Ap = boolean; + signature module StageParam { + class Ap; - class ApNil extends Ap { - ApNil() { this = false } + class ApNil extends Ap; + + bindingset[result, ap] + ApApprox getApprox(Ap ap); + + ApNil getApNil(NodeEx node); + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail); + + Content getHeadContent(Ap ap); + + class ApOption; + + ApOption apNone(); + + ApOption apSome(Ap ap); + + class Cc; + + class CcCall extends Cc; + + // TODO: member predicate on CcCall + predicate matchesCall(CcCall cc, DataFlowCall call); + + class CcNoCall extends Cc; + + Cc ccNone(); + + CcCall ccSomeCall(); + + class LocalCc; + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc); + + bindingset[node1, state1, config] + bindingset[node2, state2, config] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ); + + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + ); + + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ); + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config); + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType); } - bindingset[result, ap] - private ApApprox getApprox(Ap ap) { any() } + module Stage implements StageSig { + import Param - private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) } + /* Begin: Stage logic. */ + bindingset[result, apa] + private ApApprox unbindApa(ApApprox apa) { + pragma[only_bind_out](apa) = pragma[only_bind_out](result) + } - bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, + Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, + pragma[only_bind_into](config)) and + matchesCall(ccc, call) + } - pragma[inline] - private Content getHeadContent(Ap ap) { exists(result) and ap = true } + /** + * Holds if `node` is reachable with access path `ap` from a source in the + * configuration `config`. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. + */ + pragma[nomagic] + predicate fwdFlow( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + fwdFlow0(node, state, cc, argAp, ap, config) and + PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and + filter(node, state, ap, config) + } - class ApOption = BooleanOption; + pragma[nomagic] + private predicate fwdFlow0( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + sourceNode(node, state, config) and + (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and + argAp = apNone() and + ap = getApNil(node) + or + exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | + fwdFlow(mid, state0, cc, argAp, ap0, config) and + localCc = getLocalCc(mid, cc) + | + localStep(mid, state0, node, state, true, _, config, localCc) and + ap = ap0 + or + localStep(mid, state0, node, state, false, ap, config, localCc) and + ap0 instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + jumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStateStep(mid, state0, node, state, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + // store + exists(TypedContent tc, Ap ap0 | + fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + ap = apCons(tc, ap0) + ) + or + // read + exists(Ap ap0, Content c | + fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowConsCand(ap0, c, ap, config) + ) + or + // flow into a callable + exists(ApApprox apa | + fwdFlowIn(_, node, state, _, cc, _, ap, config) and + apa = getApprox(ap) and + if PrevStage::parameterMayFlowThrough(node, _, apa, config) + then argAp = apSome(ap) + else argAp = apNone() + ) + or + // flow out of a callable + fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) + ) + } - ApOption apNone() { result = TBooleanNone() } + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + exists(DataFlowType contentType | + fwdFlow(node1, state, cc, argAp, ap1, config) and + PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and + typecheckStore(ap1, contentType) + ) + } - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + /** + * Holds if forward flow with access path `tail` reaches a store of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(TypedContent tc | + fwdFlowStore(_, tail, tc, _, _, _, _, config) and + tc.getContent() = c and + cons = apCons(tc, tail) + ) + } + pragma[nomagic] + private predicate fwdFlowRead( + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + fwdFlow(node1, state, cc, argAp, ap, config) and + PrevStage::readStepCand(node1, c, node2, config) and + getHeadContent(ap) = c + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, + Ap ap, Configuration config + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, state, outercc, argAp, ap, config) and + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutNotFromArg( + NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + ) { + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, argAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + ccOut = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + ) + } + + pragma[nomagic] + private predicate storeStepFwd( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config + ) { + fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + ap2 = apCons(tc, ap1) and + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + } + + private predicate readStepFwd( + NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config + ) { + fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowConsCand(ap1, c, ap2, config) + } + + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and + fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), + pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + + pragma[nomagic] + private predicate returnNodeMayFlowThrough( + RetNodeEx ret, FlowState state, Ap ap, Configuration config + ) { + fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from the + * enclosing callable in order to reach a sink, and if so, `returnAp` records + * the access path of the returned value. + */ + pragma[nomagic] + predicate revFlow( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + revFlow0(node, state, toReturn, returnAp, ap, config) and + fwdFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + fwdFlow(node, state, _, _, ap, config) and + sinkNode(node, state, config) and + (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid, FlowState state0 | + localStep(node, state, mid, state0, true, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, ap, config) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, state, _, _, ap, config) and + toReturn = false and + returnAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStep(node, mid, config) and + revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStateStep(node, state, mid, state0, config) and + revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, + pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowConsCand(ap0, c, ap, config) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, state, toReturn, returnAp, ap0, config) and + readStepFwd(node, ap, _, mid, ap0, config) + ) + or + // flow into a callable + revFlowInNotToReturn(node, state, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + or + // flow out of a callable + revFlowOut(_, node, state, _, _, ap, config) and + toReturn = true and + if returnNodeMayFlowThrough(node, state, ap, config) + then returnAp = apSome(ap) + else returnAp = apNone() + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, + boolean toReturn, ApOption returnAp, Configuration config + ) { + revFlow(mid, state, toReturn, returnAp, ap0, config) and + storeStepFwd(node, ap, tc, mid, ap0, config) and + tc.getContent() = c + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, _, tail, config) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0, config) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, + Configuration config + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, state, toReturn, returnAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInNotToReturn( + ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, FlowState state, CcCall ccc | + revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and + fwdFlow(ret, state, ccc, apSome(_), ap, config) and + matchesCall(ccc, call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Ap ap2, Content c | + PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and + revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and + revFlowConsCand(ap2, c, ap1, config) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and + readStepFwd(node1, ap1, c, node2, ap2, config) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, + pragma[only_bind_into](config)) + ) + } + + predicate revFlow(NodeEx node, FlowState state, Configuration config) { + revFlow(node, state, _, _, _, config) + } + + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, ap, config) + } + + private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepFwd(_, ap, tc, _, _, config) + } + + private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepCand(_, ap, tc, _, _, config) + } + + private predicate validAp(Ap ap, Configuration config) { + revFlow(_, _, _, _, ap, config) and ap instanceof ApNil + or + exists(TypedContent head, Ap tail | + consCand(head, tail, config) and + ap = apCons(head, tail) + ) + } + + predicate consCand(TypedContent tc, Ap ap, Configuration config) { + revConsCand(tc, ap, config) and + validAp(ap, config) + } + + pragma[noinline] + private predicate parameterFlow( + ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ) { + revFlow(p, _, true, apSome(ap0), ap, config) and + c = p.getEnclosingCallable() + } + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | + parameterFlow(p, ap, ap0, c, config) and + c = ret.getEnclosingCallable() and + revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), + pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and + fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and + kind = ret.getKind() and + p.getPosition() = pos and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists( + Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + | + revFlow(arg, state, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + + predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, argAp, ap, config) + ) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | consCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and + states = count(FlowState state | revFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | + revFlow(n, state, b, retAp, ap, config) + ) + } + /* End: Stage logic. */ + } +} + +private module BooleanCallContext { + class Cc extends boolean { + Cc() { this in [true, false] } + } + + class CcCall extends Cc { + CcCall() { this = true } + } + + /** Holds if the call context may be `call`. */ + predicate matchesCall(CcCall cc, DataFlowCall call) { any() } + + class CcNoCall extends Cc { + CcNoCall() { this = false } + } + + Cc ccNone() { result = false } + + CcCall ccSomeCall() { result = true } + + class LocalCc = Unit; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } +} + +private module Level1CallContext { class Cc = CallContext; class CcCall = CallContextCall; + pragma[inline] + predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } + class CcNoCall = CallContextNoCall; Cc ccNone() { result instanceof CallContextAny } CcCall ccSomeCall() { result instanceof CallContextSomeCall } - private class LocalCc = Unit; + module NoLocalCallContext { + class LocalCc = Unit; - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSiteDispatch(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSiteDispatch(call, c) + then result = TSpecificCall(call) + else result = TSomeCall() + } + } + + module LocalCallContext { + class LocalCc = LocalCallContext; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { + result = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + node.getEnclosingCallable()) + } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() + } } bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { checkCallContextReturn(innercc, c, call) and if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() } +} - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } +private module Stage2Param implements MkStage::StageParam { + private module PrevStage = Stage1; + + class Ap extends boolean { + Ap() { this in [true, false] } + } + + class ApNil extends Ap { + ApNil() { this = false } + } + + bindingset[result, ap] + PrevStage::Ap getApprox(Ap ap) { any() } + + ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + + pragma[inline] + Content getHeadContent(Ap ap) { exists(result) and ap = true } + + class ApOption = BooleanOption; + + ApOption apNone() { result = TBooleanNone() } + + ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + + import Level1CallContext + import NoLocalCallContext bindingset[node1, state1, config] bindingset[node2, state2, config] - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -1221,9 +1906,9 @@ private module Stage2 { exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/5; - private predicate flowIntoCall = flowIntoCallNodeCand1/5; + predicate flowIntoCall = flowIntoCallNodeCand1/5; pragma[nomagic] private predicate expectsContentCand(NodeEx node, Configuration config) { @@ -1235,7 +1920,7 @@ private module Stage2 { } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and not stateBarrier(node, state, config) and @@ -1248,544 +1933,11 @@ private module Stage2 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 2 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 2 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage2 = MkStage::Stage; + pragma[nomagic] private predicate flowOutOfCallNodeCand2( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config @@ -1883,14 +2035,13 @@ private module LocalFlowBigStep { ) { additionalLocalFlowStepNodeCand1(node1, node2, config) and state1 = state2 and - Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false, - pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false, + Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false, pragma[only_bind_into](config)) or additionalLocalStateStep(node1, state1, node2, state2, config) and - Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config)) + Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config)) } /** @@ -1967,26 +2118,24 @@ private module LocalFlowBigStep { private import LocalFlowBigStep -private module Stage3 { - module PrevStage = Stage2; - - class ApApprox = PrevStage::Ap; +private module Stage3Param implements MkStage::StageParam { + private module PrevStage = Stage2; class Ap = AccessPathFront; class ApNil = AccessPathFrontNil; - private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } + PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } + Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathFrontOption; @@ -1994,44 +2143,18 @@ private module Stage3 { ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - class Cc = boolean; + import BooleanCallContext - class CcCall extends Cc { - CcCall() { this = true } - - /** Holds if this call context may be `call`. */ - predicate matchesCall(DataFlowCall call) { any() } - } - - class CcNoCall extends Cc { - CcNoCall() { this = false } - } - - Cc ccNone() { result = false } - - CcCall ccSomeCall() { result = true } - - private class LocalCc = Unit; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/5; - private predicate flowIntoCall = flowIntoCallNodeCand2/5; + predicate flowIntoCall = flowIntoCallNodeCand2/5; pragma[nomagic] private predicate clearSet(NodeEx node, ContentSet c, Configuration config) { @@ -2067,7 +2190,7 @@ private module Stage3 { private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { exists(state) and exists(config) and not clear(node, ap, config) and @@ -2080,548 +2203,15 @@ private module Stage3 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { + predicate typecheckStore(Ap ap, DataFlowType contentType) { // We need to typecheck stores here, since reverse flow through a getter // might have a different type here compared to inside the getter. compatibleTypes(ap.getType(), contentType) } - - /* Begin: Stage 3 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 3 logic. */ } +private module Stage3 = MkStage::Stage; + /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` * and remains relevant for the following pruning stage. @@ -2644,7 +2234,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and nodes = strictcount(NodeEx n, FlowState state | - Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) or flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) ) and @@ -2828,26 +2418,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } } -private module Stage4 { - module PrevStage = Stage3; - - class ApApprox = PrevStage::Ap; +private module Stage4Param implements MkStage::StageParam { + private module PrevStage = Stage3; class Ap = AccessPathApprox; class ApNil = AccessPathApproxNil; - private ApApprox getApprox(Ap ap) { result = ap.getFront() } + PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } + Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathApproxOption; @@ -2855,38 +2443,10 @@ private module Stage4 { ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - class Cc = CallContext; + import Level1CallContext + import LocalCallContext - class CcCall = CallContextCall; - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - private class LocalCc = LocalCallContext; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() - } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { - result = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - node.getEnclosingCallable()) - } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -2894,575 +2454,40 @@ private module Stage4 { } pragma[nomagic] - private predicate flowOutOfCall( + predicate flowOutOfCall( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } pragma[nomagic] - private predicate flowIntoCall( + predicate flowIntoCall( DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } // Type checking is not necessary here as it has already been done in stage 3. bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 4 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 4 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage4 = MkStage::Stage; + bindingset[conf, result] private Configuration unbindConf(Configuration conf) { exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) @@ -3495,7 +2520,7 @@ private newtype TSummaryCtx = TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and - Stage4::revFlow(p, state, _, _, _, config) + Stage4::revFlow(p, state, _, config) ) } @@ -3553,7 +2578,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { result = strictcount(NodeEx n, FlowState state | - Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config) + Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config) ) } @@ -3667,7 +2692,7 @@ private newtype TPathNode = exists(PathNodeMid mid | pathStep(mid, node, state, cc, sc, ap) and pragma[only_bind_into](config) = mid.getConfiguration() and - Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config)) + Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config)) ) } or TPathNodeSink(NodeEx node, FlowState state, Configuration config) { @@ -4207,7 +3232,7 @@ private NodeEx getAnOutNodeFlow( ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result.asNode() = kind.getAnOutNode(call) and - Stage4::revFlow(result, _, _, _, apa, config) + Stage4::revFlow(result, _, apa, config) } /** @@ -4243,7 +3268,7 @@ private predicate parameterCand( DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config ) { exists(ParamNodeEx p | - Stage4::revFlow(p, _, _, _, apa, config) and + Stage4::revFlow(p, _, apa, config) and p.isParameterOf(callable, pos) ) } diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll index a076f7a2e45..22c3bd2466e 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,12 +944,9 @@ private module Stage1 { predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) } bindingset[node, state, config] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, toReturn, pragma[only_bind_into](config)) and + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, _, pragma[only_bind_into](config)) and exists(state) and - exists(returnAp) and exists(ap) } @@ -1142,66 +1139,754 @@ private predicate flowIntoCallNodeCand1( ) } -private module Stage2 { - module PrevStage = Stage1; +private signature module StageSig { + class Ap; + predicate revFlow(NodeEx node, Configuration config); + + bindingset[node, state, config] + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config); + + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ); + + predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config); +} + +private module MkStage { class ApApprox = PrevStage::Ap; - class Ap = boolean; + signature module StageParam { + class Ap; - class ApNil extends Ap { - ApNil() { this = false } + class ApNil extends Ap; + + bindingset[result, ap] + ApApprox getApprox(Ap ap); + + ApNil getApNil(NodeEx node); + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail); + + Content getHeadContent(Ap ap); + + class ApOption; + + ApOption apNone(); + + ApOption apSome(Ap ap); + + class Cc; + + class CcCall extends Cc; + + // TODO: member predicate on CcCall + predicate matchesCall(CcCall cc, DataFlowCall call); + + class CcNoCall extends Cc; + + Cc ccNone(); + + CcCall ccSomeCall(); + + class LocalCc; + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc); + + bindingset[node1, state1, config] + bindingset[node2, state2, config] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ); + + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + ); + + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ); + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config); + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType); } - bindingset[result, ap] - private ApApprox getApprox(Ap ap) { any() } + module Stage implements StageSig { + import Param - private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) } + /* Begin: Stage logic. */ + bindingset[result, apa] + private ApApprox unbindApa(ApApprox apa) { + pragma[only_bind_out](apa) = pragma[only_bind_out](result) + } - bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, + Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, + pragma[only_bind_into](config)) and + matchesCall(ccc, call) + } - pragma[inline] - private Content getHeadContent(Ap ap) { exists(result) and ap = true } + /** + * Holds if `node` is reachable with access path `ap` from a source in the + * configuration `config`. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. + */ + pragma[nomagic] + predicate fwdFlow( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + fwdFlow0(node, state, cc, argAp, ap, config) and + PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and + filter(node, state, ap, config) + } - class ApOption = BooleanOption; + pragma[nomagic] + private predicate fwdFlow0( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + sourceNode(node, state, config) and + (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and + argAp = apNone() and + ap = getApNil(node) + or + exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | + fwdFlow(mid, state0, cc, argAp, ap0, config) and + localCc = getLocalCc(mid, cc) + | + localStep(mid, state0, node, state, true, _, config, localCc) and + ap = ap0 + or + localStep(mid, state0, node, state, false, ap, config, localCc) and + ap0 instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + jumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStateStep(mid, state0, node, state, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + // store + exists(TypedContent tc, Ap ap0 | + fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + ap = apCons(tc, ap0) + ) + or + // read + exists(Ap ap0, Content c | + fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowConsCand(ap0, c, ap, config) + ) + or + // flow into a callable + exists(ApApprox apa | + fwdFlowIn(_, node, state, _, cc, _, ap, config) and + apa = getApprox(ap) and + if PrevStage::parameterMayFlowThrough(node, _, apa, config) + then argAp = apSome(ap) + else argAp = apNone() + ) + or + // flow out of a callable + fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) + ) + } - ApOption apNone() { result = TBooleanNone() } + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + exists(DataFlowType contentType | + fwdFlow(node1, state, cc, argAp, ap1, config) and + PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and + typecheckStore(ap1, contentType) + ) + } - ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + /** + * Holds if forward flow with access path `tail` reaches a store of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(TypedContent tc | + fwdFlowStore(_, tail, tc, _, _, _, _, config) and + tc.getContent() = c and + cons = apCons(tc, tail) + ) + } + pragma[nomagic] + private predicate fwdFlowRead( + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + fwdFlow(node1, state, cc, argAp, ap, config) and + PrevStage::readStepCand(node1, c, node2, config) and + getHeadContent(ap) = c + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, + Ap ap, Configuration config + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, state, outercc, argAp, ap, config) and + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutNotFromArg( + NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + ) { + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, argAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + ccOut = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + ) + } + + pragma[nomagic] + private predicate storeStepFwd( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config + ) { + fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + ap2 = apCons(tc, ap1) and + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + } + + private predicate readStepFwd( + NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config + ) { + fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowConsCand(ap1, c, ap2, config) + } + + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and + fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), + pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + + pragma[nomagic] + private predicate returnNodeMayFlowThrough( + RetNodeEx ret, FlowState state, Ap ap, Configuration config + ) { + fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from the + * enclosing callable in order to reach a sink, and if so, `returnAp` records + * the access path of the returned value. + */ + pragma[nomagic] + predicate revFlow( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + revFlow0(node, state, toReturn, returnAp, ap, config) and + fwdFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + fwdFlow(node, state, _, _, ap, config) and + sinkNode(node, state, config) and + (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid, FlowState state0 | + localStep(node, state, mid, state0, true, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, ap, config) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, state, _, _, ap, config) and + toReturn = false and + returnAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStep(node, mid, config) and + revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStateStep(node, state, mid, state0, config) and + revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, + pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowConsCand(ap0, c, ap, config) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, state, toReturn, returnAp, ap0, config) and + readStepFwd(node, ap, _, mid, ap0, config) + ) + or + // flow into a callable + revFlowInNotToReturn(node, state, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + or + // flow out of a callable + revFlowOut(_, node, state, _, _, ap, config) and + toReturn = true and + if returnNodeMayFlowThrough(node, state, ap, config) + then returnAp = apSome(ap) + else returnAp = apNone() + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, + boolean toReturn, ApOption returnAp, Configuration config + ) { + revFlow(mid, state, toReturn, returnAp, ap0, config) and + storeStepFwd(node, ap, tc, mid, ap0, config) and + tc.getContent() = c + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, _, tail, config) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0, config) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, + Configuration config + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, state, toReturn, returnAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInNotToReturn( + ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, FlowState state, CcCall ccc | + revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and + fwdFlow(ret, state, ccc, apSome(_), ap, config) and + matchesCall(ccc, call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Ap ap2, Content c | + PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and + revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and + revFlowConsCand(ap2, c, ap1, config) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and + readStepFwd(node1, ap1, c, node2, ap2, config) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, + pragma[only_bind_into](config)) + ) + } + + predicate revFlow(NodeEx node, FlowState state, Configuration config) { + revFlow(node, state, _, _, _, config) + } + + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, ap, config) + } + + private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepFwd(_, ap, tc, _, _, config) + } + + private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepCand(_, ap, tc, _, _, config) + } + + private predicate validAp(Ap ap, Configuration config) { + revFlow(_, _, _, _, ap, config) and ap instanceof ApNil + or + exists(TypedContent head, Ap tail | + consCand(head, tail, config) and + ap = apCons(head, tail) + ) + } + + predicate consCand(TypedContent tc, Ap ap, Configuration config) { + revConsCand(tc, ap, config) and + validAp(ap, config) + } + + pragma[noinline] + private predicate parameterFlow( + ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ) { + revFlow(p, _, true, apSome(ap0), ap, config) and + c = p.getEnclosingCallable() + } + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | + parameterFlow(p, ap, ap0, c, config) and + c = ret.getEnclosingCallable() and + revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), + pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and + fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and + kind = ret.getKind() and + p.getPosition() = pos and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists( + Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + | + revFlow(arg, state, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + + predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, argAp, ap, config) + ) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | consCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and + states = count(FlowState state | revFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | + revFlow(n, state, b, retAp, ap, config) + ) + } + /* End: Stage logic. */ + } +} + +private module BooleanCallContext { + class Cc extends boolean { + Cc() { this in [true, false] } + } + + class CcCall extends Cc { + CcCall() { this = true } + } + + /** Holds if the call context may be `call`. */ + predicate matchesCall(CcCall cc, DataFlowCall call) { any() } + + class CcNoCall extends Cc { + CcNoCall() { this = false } + } + + Cc ccNone() { result = false } + + CcCall ccSomeCall() { result = true } + + class LocalCc = Unit; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } +} + +private module Level1CallContext { class Cc = CallContext; class CcCall = CallContextCall; + pragma[inline] + predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } + class CcNoCall = CallContextNoCall; Cc ccNone() { result instanceof CallContextAny } CcCall ccSomeCall() { result instanceof CallContextSomeCall } - private class LocalCc = Unit; + module NoLocalCallContext { + class LocalCc = Unit; - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSiteDispatch(call, c) - then result = TSpecificCall(call) - else result = TSomeCall() + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSiteDispatch(call, c) + then result = TSpecificCall(call) + else result = TSomeCall() + } + } + + module LocalCallContext { + class LocalCc = LocalCallContext; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { + result = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + node.getEnclosingCallable()) + } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() + } } bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { checkCallContextReturn(innercc, c, call) and if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() } +} - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } +private module Stage2Param implements MkStage::StageParam { + private module PrevStage = Stage1; + + class Ap extends boolean { + Ap() { this in [true, false] } + } + + class ApNil extends Ap { + ApNil() { this = false } + } + + bindingset[result, ap] + PrevStage::Ap getApprox(Ap ap) { any() } + + ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + + pragma[inline] + Content getHeadContent(Ap ap) { exists(result) and ap = true } + + class ApOption = BooleanOption; + + ApOption apNone() { result = TBooleanNone() } + + ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + + import Level1CallContext + import NoLocalCallContext bindingset[node1, state1, config] bindingset[node2, state2, config] - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -1221,9 +1906,9 @@ private module Stage2 { exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + predicate flowOutOfCall = flowOutOfCallNodeCand1/5; - private predicate flowIntoCall = flowIntoCallNodeCand1/5; + predicate flowIntoCall = flowIntoCallNodeCand1/5; pragma[nomagic] private predicate expectsContentCand(NodeEx node, Configuration config) { @@ -1235,7 +1920,7 @@ private module Stage2 { } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { PrevStage::revFlowState(state, pragma[only_bind_into](config)) and exists(ap) and not stateBarrier(node, state, config) and @@ -1248,544 +1933,11 @@ private module Stage2 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 2 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 2 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage2 = MkStage::Stage; + pragma[nomagic] private predicate flowOutOfCallNodeCand2( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config @@ -1883,14 +2035,13 @@ private module LocalFlowBigStep { ) { additionalLocalFlowStepNodeCand1(node1, node2, config) and state1 = state2 and - Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false, - pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false, + Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false, pragma[only_bind_into](config)) or additionalLocalStateStep(node1, state1, node2, state2, config) and - Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and - Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config)) + Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config)) } /** @@ -1967,26 +2118,24 @@ private module LocalFlowBigStep { private import LocalFlowBigStep -private module Stage3 { - module PrevStage = Stage2; - - class ApApprox = PrevStage::Ap; +private module Stage3Param implements MkStage::StageParam { + private module PrevStage = Stage2; class Ap = AccessPathFront; class ApNil = AccessPathFrontNil; - private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } + PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } + Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathFrontOption; @@ -1994,44 +2143,18 @@ private module Stage3 { ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } - class Cc = boolean; + import BooleanCallContext - class CcCall extends Cc { - CcCall() { this = true } - - /** Holds if this call context may be `call`. */ - predicate matchesCall(DataFlowCall call) { any() } - } - - class CcNoCall extends Cc { - CcNoCall() { this = false } - } - - Cc ccNone() { result = false } - - CcCall ccSomeCall() { result = true } - - private class LocalCc = Unit; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { any() } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) } - private predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + predicate flowOutOfCall = flowOutOfCallNodeCand2/5; - private predicate flowIntoCall = flowIntoCallNodeCand2/5; + predicate flowIntoCall = flowIntoCallNodeCand2/5; pragma[nomagic] private predicate clearSet(NodeEx node, ContentSet c, Configuration config) { @@ -2067,7 +2190,7 @@ private module Stage3 { private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { exists(state) and exists(config) and not clear(node, ap, config) and @@ -2080,548 +2203,15 @@ private module Stage3 { } bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { + predicate typecheckStore(Ap ap, DataFlowType contentType) { // We need to typecheck stores here, since reverse flow through a getter // might have a different type here compared to inside the getter. compatibleTypes(ap.getType(), contentType) } - - /* Begin: Stage 3 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 3 logic. */ } +private module Stage3 = MkStage::Stage; + /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` * and remains relevant for the following pruning stage. @@ -2644,7 +2234,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and nodes = strictcount(NodeEx n, FlowState state | - Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) or flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) ) and @@ -2828,26 +2418,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } } -private module Stage4 { - module PrevStage = Stage3; - - class ApApprox = PrevStage::Ap; +private module Stage4Param implements MkStage::StageParam { + private module PrevStage = Stage3; class Ap = AccessPathApprox; class ApNil = AccessPathApproxNil; - private ApApprox getApprox(Ap ap) { result = ap.getFront() } + PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } - private ApNil getApNil(NodeEx node) { + ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType()) } bindingset[tc, tail] - private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } + Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } pragma[noinline] - private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } class ApOption = AccessPathApproxOption; @@ -2855,38 +2443,10 @@ private module Stage4 { ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } - class Cc = CallContext; + import Level1CallContext + import LocalCallContext - class CcCall = CallContextCall; - - class CcNoCall = CallContextNoCall; - - Cc ccNone() { result instanceof CallContextAny } - - CcCall ccSomeCall() { result instanceof CallContextSomeCall } - - private class LocalCc = LocalCallContext; - - bindingset[call, c, outercc] - private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { - checkCallContextCall(outercc, call, c) and - if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() - } - - bindingset[call, c, innercc] - private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { - checkCallContextReturn(innercc, c, call) and - if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() - } - - bindingset[node, cc] - private LocalCc getLocalCc(NodeEx node, Cc cc) { - result = - getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), - node.getEnclosingCallable()) - } - - private predicate localStep( + predicate localStep( NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc ) { @@ -2894,575 +2454,40 @@ private module Stage4 { } pragma[nomagic] - private predicate flowOutOfCall( + predicate flowOutOfCall( DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } pragma[nomagic] - private predicate flowIntoCall( + predicate flowIntoCall( DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, Configuration config ) { exists(FlowState state | flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and - PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _, - pragma[only_bind_into](config)) and - PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _, + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) ) } bindingset[node, state, ap, config] - private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } // Type checking is not necessary here as it has already been done in stage 3. bindingset[ap, contentType] - private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } - - /* Begin: Stage 4 logic. */ - bindingset[node, state, config] - private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) { - PrevStage::revFlow(node, state, _, _, apa, config) - } - - bindingset[result, apa] - private ApApprox unbindApa(ApApprox apa) { - pragma[only_bind_out](apa) = pragma[only_bind_out](result) - } - - pragma[nomagic] - private predicate flowThroughOutOfCall( - DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, - Configuration config - ) { - flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and - PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, - pragma[only_bind_into](config)) and - ccc.matchesCall(call) - } - - /** - * Holds if `node` is reachable with access path `ap` from a source in the - * configuration `config`. - * - * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. - */ - pragma[nomagic] - predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) { - fwdFlow0(node, state, cc, argAp, ap, config) and - flowCand(node, state, unbindApa(getApprox(ap)), config) and - filter(node, state, ap, config) - } - - pragma[nomagic] - private predicate fwdFlow0( - NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - sourceNode(node, state, config) and - (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and - argAp = apNone() and - ap = getApNil(node) - or - exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | - fwdFlow(mid, state0, cc, argAp, ap0, config) and - localCc = getLocalCc(mid, cc) - | - localStep(mid, state0, node, state, true, _, config, localCc) and - ap = ap0 - or - localStep(mid, state0, node, state, false, ap, config, localCc) and - ap0 instanceof ApNil - ) - or - exists(NodeEx mid | - fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - jumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStep(mid, node, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and - additionalJumpStateStep(mid, state0, node, state, config) and - cc = ccNone() and - argAp = apNone() and - ap = getApNil(node) - ) - or - // store - exists(TypedContent tc, Ap ap0 | - fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and - ap = apCons(tc, ap0) - ) - or - // read - exists(Ap ap0, Content c | - fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and - fwdFlowConsCand(ap0, c, ap, config) - ) - or - // flow into a callable - exists(ApApprox apa | - fwdFlowIn(_, node, state, _, cc, _, ap, config) and - apa = getApprox(ap) and - if PrevStage::parameterMayFlowThrough(node, _, apa, config) - then argAp = apSome(ap) - else argAp = apNone() - ) - or - // flow out of a callable - fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) - or - exists(DataFlowCall call, Ap argAp0 | - fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) - } - - pragma[nomagic] - private predicate fwdFlowStore( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - exists(DataFlowType contentType | - fwdFlow(node1, state, cc, argAp, ap1, config) and - PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and - typecheckStore(ap1, contentType) - ) - } - - /** - * Holds if forward flow with access path `tail` reaches a store of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(TypedContent tc | - fwdFlowStore(_, tail, tc, _, _, _, _, config) and - tc.getContent() = c and - cons = apCons(tc, tail) - ) - } - - pragma[nomagic] - private predicate fwdFlowRead( - Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, - Configuration config - ) { - fwdFlow(node1, state, cc, argAp, ap, config) and - PrevStage::readStepCand(node1, c, node2, config) and - getHeadContent(ap) = c - } - - pragma[nomagic] - private predicate fwdFlowIn( - DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, - Ap ap, Configuration config - ) { - exists(ArgNodeEx arg, boolean allowsFieldFlow | - fwdFlow(arg, state, outercc, argAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutNotFromArg( - NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config - ) { - exists( - DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, - DataFlowCallable inner - | - fwdFlow(ret, state, innercc, argAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - inner = ret.getEnclosingCallable() and - ccOut = getCallContextReturn(inner, call, innercc) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate fwdFlowOutFromArg( - DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | - fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and - flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` - * and data might flow through the target callable and back out at `call`. - */ - pragma[nomagic] - private predicate fwdFlowIsEntered( - DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p | - fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and - PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) - ) - } - - pragma[nomagic] - private predicate storeStepFwd( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config - ) { - fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and - ap2 = apCons(tc, ap1) and - fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) - } - - private predicate readStepFwd( - NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config - ) { - fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and - fwdFlowConsCand(ap1, c, ap2, config) - } - - pragma[nomagic] - private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { - exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, - pragma[only_bind_into](config)) and - fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and - fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), - pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), - pragma[only_bind_into](config)) - ) - } - - pragma[nomagic] - private predicate flowThroughIntoCall( - DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config - ) { - flowIntoCall(call, arg, p, allowsFieldFlow, config) and - fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and - PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and - callMayFlowThroughFwd(call, pragma[only_bind_into](config)) - } - - pragma[nomagic] - private predicate returnNodeMayFlowThrough( - RetNodeEx ret, FlowState state, Ap ap, Configuration config - ) { - fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) - } - - /** - * Holds if `node` with access path `ap` is part of a path from a source to a - * sink in the configuration `config`. - * - * The Boolean `toReturn` records whether the node must be returned from the - * enclosing callable in order to reach a sink, and if so, `returnAp` records - * the access path of the returned value. - */ - pragma[nomagic] - predicate revFlow( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow0(node, state, toReturn, returnAp, ap, config) and - fwdFlow(node, state, _, _, ap, config) - } - - pragma[nomagic] - private predicate revFlow0( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - fwdFlow(node, state, _, _, ap, config) and - sinkNode(node, state, config) and - (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and - returnAp = apNone() and - ap instanceof ApNil - or - exists(NodeEx mid, FlowState state0 | - localStep(node, state, mid, state0, true, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, ap, config) - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and - localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and - revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and - ap instanceof ApNil - ) - or - exists(NodeEx mid | - jumpStep(node, mid, config) and - revFlow(mid, state, _, _, ap, config) and - toReturn = false and - returnAp = apNone() - ) - or - exists(NodeEx mid, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStep(node, mid, config) and - revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - exists(NodeEx mid, FlowState state0, ApNil nil | - fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and - additionalJumpStateStep(node, state, mid, state0, config) and - revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, - pragma[only_bind_into](config)) and - toReturn = false and - returnAp = apNone() and - ap instanceof ApNil - ) - or - // store - exists(Ap ap0, Content c | - revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and - revFlowConsCand(ap0, c, ap, config) - ) - or - // read - exists(NodeEx mid, Ap ap0 | - revFlow(mid, state, toReturn, returnAp, ap0, config) and - readStepFwd(node, ap, _, mid, ap0, config) - ) - or - // flow into a callable - revFlowInNotToReturn(node, state, returnAp, ap, config) and - toReturn = false - or - exists(DataFlowCall call, Ap returnAp0 | - revFlowInToReturn(call, node, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - or - // flow out of a callable - revFlowOut(_, node, state, _, _, ap, config) and - toReturn = true and - if returnNodeMayFlowThrough(node, state, ap, config) - then returnAp = apSome(ap) - else returnAp = apNone() - } - - pragma[nomagic] - private predicate revFlowStore( - Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, - boolean toReturn, ApOption returnAp, Configuration config - ) { - revFlow(mid, state, toReturn, returnAp, ap0, config) and - storeStepFwd(node, ap, tc, mid, ap0, config) and - tc.getContent() = c - } - - /** - * Holds if reverse flow with access path `tail` reaches a read of `c` - * resulting in access path `cons`. - */ - pragma[nomagic] - private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { - exists(NodeEx mid, Ap tail0 | - revFlow(mid, _, _, _, tail, config) and - tail = pragma[only_bind_into](tail0) and - readStepFwd(_, cons, c, mid, tail0, config) - ) - } - - pragma[nomagic] - private predicate revFlowOut( - DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(NodeEx out, boolean allowsFieldFlow | - revFlow(out, state, toReturn, returnAp, ap, config) and - flowOutOfCall(call, ret, out, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInNotToReturn( - ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, false, returnAp, ap, config) and - flowIntoCall(_, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - pragma[nomagic] - private predicate revFlowInToReturn( - DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config - ) { - exists(ParamNodeEx p, boolean allowsFieldFlow | - revFlow(p, state, true, apSome(returnAp), ap, config) and - flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and - if allowsFieldFlow = false then ap instanceof ApNil else any() - ) - } - - /** - * Holds if an output from `call` is reached in the flow covered by `revFlow` - * and data might flow through the target callable resulting in reverse flow - * reaching an argument of `call`. - */ - pragma[nomagic] - private predicate revFlowIsReturned( - DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - exists(RetNodeEx ret, FlowState state, CcCall ccc | - revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and - fwdFlow(ret, state, ccc, apSome(_), ap, config) and - ccc.matchesCall(call) - ) - } - - pragma[nomagic] - predicate storeStepCand( - NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, - Configuration config - ) { - exists(Ap ap2, Content c | - PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and - revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and - revFlowConsCand(ap2, c, ap1, config) - ) - } - - predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { - exists(Ap ap1, Ap ap2 | - revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and - readStepFwd(node1, ap1, c, node2, ap2, config) and - revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, - pragma[only_bind_into](config)) - ) - } - - predicate revFlow(NodeEx node, FlowState state, Configuration config) { - revFlow(node, state, _, _, _, config) - } - - pragma[nomagic] - predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } - - // use an alias as a workaround for bad functionality-induced joins - pragma[nomagic] - predicate revFlowAlias( - NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { - revFlow(node, state, toReturn, returnAp, ap, config) - } - - private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepFwd(_, ap, tc, _, _, config) - } - - private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { - storeStepCand(_, ap, tc, _, _, config) - } - - private predicate validAp(Ap ap, Configuration config) { - revFlow(_, _, _, _, ap, config) and ap instanceof ApNil - or - exists(TypedContent head, Ap tail | - consCand(head, tail, config) and - ap = apCons(head, tail) - ) - } - - predicate consCand(TypedContent tc, Ap ap, Configuration config) { - revConsCand(tc, ap, config) and - validAp(ap, config) - } - - pragma[noinline] - private predicate parameterFlow( - ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config - ) { - revFlow(p, _, true, apSome(ap0), ap, config) and - c = p.getEnclosingCallable() - } - - predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { - exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | - parameterFlow(p, ap, ap0, c, config) and - c = ret.getEnclosingCallable() and - revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), - pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and - fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and - kind = ret.getKind() and - p.getPosition() = pos and - // we don't expect a parameter to return stored in itself, unless explicitly allowed - ( - not kind.(ParamUpdateReturnKind).getPosition() = pos - or - p.allowParameterReturnInSelf() - ) - ) - } - - pragma[nomagic] - predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { - exists( - Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap - | - revFlow(arg, state, toReturn, returnAp, ap, config) and - revFlowInToReturn(call, arg, state, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) - } - - predicate stats( - boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config - ) { - fwd = true and - nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and - states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | - fwdFlow(n, state, cc, argAp, ap, config) - ) - or - fwd = false and - nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and - fields = count(TypedContent f0 | consCand(f0, _, config)) and - conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and - states = count(FlowState state | revFlow(_, state, _, _, _, config)) and - tuples = - count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | - revFlow(n, state, b, retAp, ap, config) - ) - } - /* End: Stage 4 logic. */ + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } +private module Stage4 = MkStage::Stage; + bindingset[conf, result] private Configuration unbindConf(Configuration conf) { exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) @@ -3495,7 +2520,7 @@ private newtype TSummaryCtx = TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { exists(Configuration config | Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and - Stage4::revFlow(p, state, _, _, _, config) + Stage4::revFlow(p, state, _, config) ) } @@ -3553,7 +2578,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { result = strictcount(NodeEx n, FlowState state | - Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config) + Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config) ) } @@ -3667,7 +2692,7 @@ private newtype TPathNode = exists(PathNodeMid mid | pathStep(mid, node, state, cc, sc, ap) and pragma[only_bind_into](config) = mid.getConfiguration() and - Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config)) + Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config)) ) } or TPathNodeSink(NodeEx node, FlowState state, Configuration config) { @@ -4207,7 +3232,7 @@ private NodeEx getAnOutNodeFlow( ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result.asNode() = kind.getAnOutNode(call) and - Stage4::revFlow(result, _, _, _, apa, config) + Stage4::revFlow(result, _, apa, config) } /** @@ -4243,7 +3268,7 @@ private predicate parameterCand( DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config ) { exists(ParamNodeEx p | - Stage4::revFlow(p, _, _, _, apa, config) and + Stage4::revFlow(p, _, apa, config) and p.isParameterOf(callable, pos) ) } From 3d47875b60ab46636c7855ba0c1def8492807ed7 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Fri, 5 Aug 2022 08:49:06 +0200 Subject: [PATCH 455/505] Dataflow: Generate shorter RA/DIL names. --- .../semmle/code/cpp/dataflow/internal/DataFlowImpl.qll | 8 ++++++-- .../semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll | 8 ++++++-- .../semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll | 8 ++++++-- .../semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll | 8 ++++++-- .../code/cpp/dataflow/internal/DataFlowImplLocal.qll | 8 ++++++-- .../semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll | 8 ++++++-- .../code/cpp/ir/dataflow/internal/DataFlowImpl2.qll | 8 ++++++-- .../code/cpp/ir/dataflow/internal/DataFlowImpl3.qll | 8 ++++++-- .../code/cpp/ir/dataflow/internal/DataFlowImpl4.qll | 8 ++++++-- .../semmle/code/csharp/dataflow/internal/DataFlowImpl.qll | 8 ++++++-- .../code/csharp/dataflow/internal/DataFlowImpl2.qll | 8 ++++++-- .../code/csharp/dataflow/internal/DataFlowImpl3.qll | 8 ++++++-- .../code/csharp/dataflow/internal/DataFlowImpl4.qll | 8 ++++++-- .../code/csharp/dataflow/internal/DataFlowImpl5.qll | 8 ++++++-- .../dataflow/internal/DataFlowImplForContentDataFlow.qll | 8 ++++++-- .../semmle/code/java/dataflow/internal/DataFlowImpl.qll | 8 ++++++-- .../semmle/code/java/dataflow/internal/DataFlowImpl2.qll | 8 ++++++-- .../semmle/code/java/dataflow/internal/DataFlowImpl3.qll | 8 ++++++-- .../semmle/code/java/dataflow/internal/DataFlowImpl4.qll | 8 ++++++-- .../semmle/code/java/dataflow/internal/DataFlowImpl5.qll | 8 ++++++-- .../semmle/code/java/dataflow/internal/DataFlowImpl6.qll | 8 ++++++-- .../dataflow/internal/DataFlowImplForOnActivityResult.qll | 8 ++++++-- .../dataflow/internal/DataFlowImplForSerializability.qll | 8 ++++++-- .../semmle/python/dataflow/new/internal/DataFlowImpl.qll | 8 ++++++-- .../semmle/python/dataflow/new/internal/DataFlowImpl2.qll | 8 ++++++-- .../semmle/python/dataflow/new/internal/DataFlowImpl3.qll | 8 ++++++-- .../semmle/python/dataflow/new/internal/DataFlowImpl4.qll | 8 ++++++-- .../ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll | 8 ++++++-- .../lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll | 8 ++++++-- .../ruby/dataflow/internal/DataFlowImplForLibraries.qll | 8 ++++++-- .../lib/codeql/swift/dataflow/internal/DataFlowImpl.qll | 8 ++++++-- 31 files changed, 186 insertions(+), 62 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll index 22c3bd2466e..340bfe280b7 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll @@ -1936,7 +1936,9 @@ private module Stage2Param implements MkStage::StageParam { predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } -private module Stage2 = MkStage::Stage; +private module Stage2 implements StageSig { + import MkStage::Stage +} pragma[nomagic] private predicate flowOutOfCallNodeCand2( @@ -2210,7 +2212,9 @@ private module Stage3Param implements MkStage::StageParam { } } -private module Stage3 = MkStage::Stage; +private module Stage3 implements StageSig { + import MkStage::Stage +} /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll index 22c3bd2466e..340bfe280b7 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll @@ -1936,7 +1936,9 @@ private module Stage2Param implements MkStage::StageParam { predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } -private module Stage2 = MkStage::Stage; +private module Stage2 implements StageSig { + import MkStage::Stage +} pragma[nomagic] private predicate flowOutOfCallNodeCand2( @@ -2210,7 +2212,9 @@ private module Stage3Param implements MkStage::StageParam { } } -private module Stage3 = MkStage::Stage; +private module Stage3 implements StageSig { + import MkStage::Stage +} /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll index 22c3bd2466e..340bfe280b7 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll @@ -1936,7 +1936,9 @@ private module Stage2Param implements MkStage::StageParam { predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } -private module Stage2 = MkStage::Stage; +private module Stage2 implements StageSig { + import MkStage::Stage +} pragma[nomagic] private predicate flowOutOfCallNodeCand2( @@ -2210,7 +2212,9 @@ private module Stage3Param implements MkStage::StageParam { } } -private module Stage3 = MkStage::Stage; +private module Stage3 implements StageSig { + import MkStage::Stage +} /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll index 22c3bd2466e..340bfe280b7 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll @@ -1936,7 +1936,9 @@ private module Stage2Param implements MkStage::StageParam { predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } -private module Stage2 = MkStage::Stage; +private module Stage2 implements StageSig { + import MkStage::Stage +} pragma[nomagic] private predicate flowOutOfCallNodeCand2( @@ -2210,7 +2212,9 @@ private module Stage3Param implements MkStage::StageParam { } } -private module Stage3 = MkStage::Stage; +private module Stage3 implements StageSig { + import MkStage::Stage +} /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll index 22c3bd2466e..340bfe280b7 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll @@ -1936,7 +1936,9 @@ private module Stage2Param implements MkStage::StageParam { predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } -private module Stage2 = MkStage::Stage; +private module Stage2 implements StageSig { + import MkStage::Stage +} pragma[nomagic] private predicate flowOutOfCallNodeCand2( @@ -2210,7 +2212,9 @@ private module Stage3Param implements MkStage::StageParam { } } -private module Stage3 = MkStage::Stage; +private module Stage3 implements StageSig { + import MkStage::Stage +} /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll index 22c3bd2466e..340bfe280b7 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll @@ -1936,7 +1936,9 @@ private module Stage2Param implements MkStage::StageParam { predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } -private module Stage2 = MkStage::Stage; +private module Stage2 implements StageSig { + import MkStage::Stage +} pragma[nomagic] private predicate flowOutOfCallNodeCand2( @@ -2210,7 +2212,9 @@ private module Stage3Param implements MkStage::StageParam { } } -private module Stage3 = MkStage::Stage; +private module Stage3 implements StageSig { + import MkStage::Stage +} /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll index 22c3bd2466e..340bfe280b7 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll @@ -1936,7 +1936,9 @@ private module Stage2Param implements MkStage::StageParam { predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } -private module Stage2 = MkStage::Stage; +private module Stage2 implements StageSig { + import MkStage::Stage +} pragma[nomagic] private predicate flowOutOfCallNodeCand2( @@ -2210,7 +2212,9 @@ private module Stage3Param implements MkStage::StageParam { } } -private module Stage3 = MkStage::Stage; +private module Stage3 implements StageSig { + import MkStage::Stage +} /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll index 22c3bd2466e..340bfe280b7 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll @@ -1936,7 +1936,9 @@ private module Stage2Param implements MkStage::StageParam { predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } -private module Stage2 = MkStage::Stage; +private module Stage2 implements StageSig { + import MkStage::Stage +} pragma[nomagic] private predicate flowOutOfCallNodeCand2( @@ -2210,7 +2212,9 @@ private module Stage3Param implements MkStage::StageParam { } } -private module Stage3 = MkStage::Stage; +private module Stage3 implements StageSig { + import MkStage::Stage +} /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll index 22c3bd2466e..340bfe280b7 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll @@ -1936,7 +1936,9 @@ private module Stage2Param implements MkStage::StageParam { predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } -private module Stage2 = MkStage::Stage; +private module Stage2 implements StageSig { + import MkStage::Stage +} pragma[nomagic] private predicate flowOutOfCallNodeCand2( @@ -2210,7 +2212,9 @@ private module Stage3Param implements MkStage::StageParam { } } -private module Stage3 = MkStage::Stage; +private module Stage3 implements StageSig { + import MkStage::Stage +} /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll index 22c3bd2466e..340bfe280b7 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -1936,7 +1936,9 @@ private module Stage2Param implements MkStage::StageParam { predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } -private module Stage2 = MkStage::Stage; +private module Stage2 implements StageSig { + import MkStage::Stage +} pragma[nomagic] private predicate flowOutOfCallNodeCand2( @@ -2210,7 +2212,9 @@ private module Stage3Param implements MkStage::StageParam { } } -private module Stage3 = MkStage::Stage; +private module Stage3 implements StageSig { + import MkStage::Stage +} /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll index 22c3bd2466e..340bfe280b7 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll @@ -1936,7 +1936,9 @@ private module Stage2Param implements MkStage::StageParam { predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } -private module Stage2 = MkStage::Stage; +private module Stage2 implements StageSig { + import MkStage::Stage +} pragma[nomagic] private predicate flowOutOfCallNodeCand2( @@ -2210,7 +2212,9 @@ private module Stage3Param implements MkStage::StageParam { } } -private module Stage3 = MkStage::Stage; +private module Stage3 implements StageSig { + import MkStage::Stage +} /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll index 22c3bd2466e..340bfe280b7 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll @@ -1936,7 +1936,9 @@ private module Stage2Param implements MkStage::StageParam { predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } -private module Stage2 = MkStage::Stage; +private module Stage2 implements StageSig { + import MkStage::Stage +} pragma[nomagic] private predicate flowOutOfCallNodeCand2( @@ -2210,7 +2212,9 @@ private module Stage3Param implements MkStage::StageParam { } } -private module Stage3 = MkStage::Stage; +private module Stage3 implements StageSig { + import MkStage::Stage +} /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll index 22c3bd2466e..340bfe280b7 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll @@ -1936,7 +1936,9 @@ private module Stage2Param implements MkStage::StageParam { predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } -private module Stage2 = MkStage::Stage; +private module Stage2 implements StageSig { + import MkStage::Stage +} pragma[nomagic] private predicate flowOutOfCallNodeCand2( @@ -2210,7 +2212,9 @@ private module Stage3Param implements MkStage::StageParam { } } -private module Stage3 = MkStage::Stage; +private module Stage3 implements StageSig { + import MkStage::Stage +} /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll index 22c3bd2466e..340bfe280b7 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll @@ -1936,7 +1936,9 @@ private module Stage2Param implements MkStage::StageParam { predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } -private module Stage2 = MkStage::Stage; +private module Stage2 implements StageSig { + import MkStage::Stage +} pragma[nomagic] private predicate flowOutOfCallNodeCand2( @@ -2210,7 +2212,9 @@ private module Stage3Param implements MkStage::StageParam { } } -private module Stage3 = MkStage::Stage; +private module Stage3 implements StageSig { + import MkStage::Stage +} /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplForContentDataFlow.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplForContentDataFlow.qll index 22c3bd2466e..340bfe280b7 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplForContentDataFlow.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplForContentDataFlow.qll @@ -1936,7 +1936,9 @@ private module Stage2Param implements MkStage::StageParam { predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } -private module Stage2 = MkStage::Stage; +private module Stage2 implements StageSig { + import MkStage::Stage +} pragma[nomagic] private predicate flowOutOfCallNodeCand2( @@ -2210,7 +2212,9 @@ private module Stage3Param implements MkStage::StageParam { } } -private module Stage3 = MkStage::Stage; +private module Stage3 implements StageSig { + import MkStage::Stage +} /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll index 22c3bd2466e..340bfe280b7 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll @@ -1936,7 +1936,9 @@ private module Stage2Param implements MkStage::StageParam { predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } -private module Stage2 = MkStage::Stage; +private module Stage2 implements StageSig { + import MkStage::Stage +} pragma[nomagic] private predicate flowOutOfCallNodeCand2( @@ -2210,7 +2212,9 @@ private module Stage3Param implements MkStage::StageParam { } } -private module Stage3 = MkStage::Stage; +private module Stage3 implements StageSig { + import MkStage::Stage +} /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll index 22c3bd2466e..340bfe280b7 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll @@ -1936,7 +1936,9 @@ private module Stage2Param implements MkStage::StageParam { predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } -private module Stage2 = MkStage::Stage; +private module Stage2 implements StageSig { + import MkStage::Stage +} pragma[nomagic] private predicate flowOutOfCallNodeCand2( @@ -2210,7 +2212,9 @@ private module Stage3Param implements MkStage::StageParam { } } -private module Stage3 = MkStage::Stage; +private module Stage3 implements StageSig { + import MkStage::Stage +} /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll index 22c3bd2466e..340bfe280b7 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll @@ -1936,7 +1936,9 @@ private module Stage2Param implements MkStage::StageParam { predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } -private module Stage2 = MkStage::Stage; +private module Stage2 implements StageSig { + import MkStage::Stage +} pragma[nomagic] private predicate flowOutOfCallNodeCand2( @@ -2210,7 +2212,9 @@ private module Stage3Param implements MkStage::StageParam { } } -private module Stage3 = MkStage::Stage; +private module Stage3 implements StageSig { + import MkStage::Stage +} /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll index 22c3bd2466e..340bfe280b7 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll @@ -1936,7 +1936,9 @@ private module Stage2Param implements MkStage::StageParam { predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } -private module Stage2 = MkStage::Stage; +private module Stage2 implements StageSig { + import MkStage::Stage +} pragma[nomagic] private predicate flowOutOfCallNodeCand2( @@ -2210,7 +2212,9 @@ private module Stage3Param implements MkStage::StageParam { } } -private module Stage3 = MkStage::Stage; +private module Stage3 implements StageSig { + import MkStage::Stage +} /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll index 22c3bd2466e..340bfe280b7 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll @@ -1936,7 +1936,9 @@ private module Stage2Param implements MkStage::StageParam { predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } -private module Stage2 = MkStage::Stage; +private module Stage2 implements StageSig { + import MkStage::Stage +} pragma[nomagic] private predicate flowOutOfCallNodeCand2( @@ -2210,7 +2212,9 @@ private module Stage3Param implements MkStage::StageParam { } } -private module Stage3 = MkStage::Stage; +private module Stage3 implements StageSig { + import MkStage::Stage +} /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll index 22c3bd2466e..340bfe280b7 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll @@ -1936,7 +1936,9 @@ private module Stage2Param implements MkStage::StageParam { predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } -private module Stage2 = MkStage::Stage; +private module Stage2 implements StageSig { + import MkStage::Stage +} pragma[nomagic] private predicate flowOutOfCallNodeCand2( @@ -2210,7 +2212,9 @@ private module Stage3Param implements MkStage::StageParam { } } -private module Stage3 = MkStage::Stage; +private module Stage3 implements StageSig { + import MkStage::Stage +} /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForOnActivityResult.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForOnActivityResult.qll index 22c3bd2466e..340bfe280b7 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForOnActivityResult.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForOnActivityResult.qll @@ -1936,7 +1936,9 @@ private module Stage2Param implements MkStage::StageParam { predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } -private module Stage2 = MkStage::Stage; +private module Stage2 implements StageSig { + import MkStage::Stage +} pragma[nomagic] private predicate flowOutOfCallNodeCand2( @@ -2210,7 +2212,9 @@ private module Stage3Param implements MkStage::StageParam { } } -private module Stage3 = MkStage::Stage; +private module Stage3 implements StageSig { + import MkStage::Stage +} /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll index 22c3bd2466e..340bfe280b7 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll @@ -1936,7 +1936,9 @@ private module Stage2Param implements MkStage::StageParam { predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } -private module Stage2 = MkStage::Stage; +private module Stage2 implements StageSig { + import MkStage::Stage +} pragma[nomagic] private predicate flowOutOfCallNodeCand2( @@ -2210,7 +2212,9 @@ private module Stage3Param implements MkStage::StageParam { } } -private module Stage3 = MkStage::Stage; +private module Stage3 implements StageSig { + import MkStage::Stage +} /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll index 22c3bd2466e..340bfe280b7 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll @@ -1936,7 +1936,9 @@ private module Stage2Param implements MkStage::StageParam { predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } -private module Stage2 = MkStage::Stage; +private module Stage2 implements StageSig { + import MkStage::Stage +} pragma[nomagic] private predicate flowOutOfCallNodeCand2( @@ -2210,7 +2212,9 @@ private module Stage3Param implements MkStage::StageParam { } } -private module Stage3 = MkStage::Stage; +private module Stage3 implements StageSig { + import MkStage::Stage +} /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll index 22c3bd2466e..340bfe280b7 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll @@ -1936,7 +1936,9 @@ private module Stage2Param implements MkStage::StageParam { predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } -private module Stage2 = MkStage::Stage; +private module Stage2 implements StageSig { + import MkStage::Stage +} pragma[nomagic] private predicate flowOutOfCallNodeCand2( @@ -2210,7 +2212,9 @@ private module Stage3Param implements MkStage::StageParam { } } -private module Stage3 = MkStage::Stage; +private module Stage3 implements StageSig { + import MkStage::Stage +} /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll index 22c3bd2466e..340bfe280b7 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll @@ -1936,7 +1936,9 @@ private module Stage2Param implements MkStage::StageParam { predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } -private module Stage2 = MkStage::Stage; +private module Stage2 implements StageSig { + import MkStage::Stage +} pragma[nomagic] private predicate flowOutOfCallNodeCand2( @@ -2210,7 +2212,9 @@ private module Stage3Param implements MkStage::StageParam { } } -private module Stage3 = MkStage::Stage; +private module Stage3 implements StageSig { + import MkStage::Stage +} /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll index 22c3bd2466e..340bfe280b7 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll @@ -1936,7 +1936,9 @@ private module Stage2Param implements MkStage::StageParam { predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } -private module Stage2 = MkStage::Stage; +private module Stage2 implements StageSig { + import MkStage::Stage +} pragma[nomagic] private predicate flowOutOfCallNodeCand2( @@ -2210,7 +2212,9 @@ private module Stage3Param implements MkStage::StageParam { } } -private module Stage3 = MkStage::Stage; +private module Stage3 implements StageSig { + import MkStage::Stage +} /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll index 22c3bd2466e..340bfe280b7 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll @@ -1936,7 +1936,9 @@ private module Stage2Param implements MkStage::StageParam { predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } -private module Stage2 = MkStage::Stage; +private module Stage2 implements StageSig { + import MkStage::Stage +} pragma[nomagic] private predicate flowOutOfCallNodeCand2( @@ -2210,7 +2212,9 @@ private module Stage3Param implements MkStage::StageParam { } } -private module Stage3 = MkStage::Stage; +private module Stage3 implements StageSig { + import MkStage::Stage +} /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll index 22c3bd2466e..340bfe280b7 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll @@ -1936,7 +1936,9 @@ private module Stage2Param implements MkStage::StageParam { predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } -private module Stage2 = MkStage::Stage; +private module Stage2 implements StageSig { + import MkStage::Stage +} pragma[nomagic] private predicate flowOutOfCallNodeCand2( @@ -2210,7 +2212,9 @@ private module Stage3Param implements MkStage::StageParam { } } -private module Stage3 = MkStage::Stage; +private module Stage3 implements StageSig { + import MkStage::Stage +} /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForLibraries.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForLibraries.qll index 22c3bd2466e..340bfe280b7 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForLibraries.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForLibraries.qll @@ -1936,7 +1936,9 @@ private module Stage2Param implements MkStage::StageParam { predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } -private module Stage2 = MkStage::Stage; +private module Stage2 implements StageSig { + import MkStage::Stage +} pragma[nomagic] private predicate flowOutOfCallNodeCand2( @@ -2210,7 +2212,9 @@ private module Stage3Param implements MkStage::StageParam { } } -private module Stage3 = MkStage::Stage; +private module Stage3 implements StageSig { + import MkStage::Stage +} /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll index 22c3bd2466e..340bfe280b7 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll @@ -1936,7 +1936,9 @@ private module Stage2Param implements MkStage::StageParam { predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } } -private module Stage2 = MkStage::Stage; +private module Stage2 implements StageSig { + import MkStage::Stage +} pragma[nomagic] private predicate flowOutOfCallNodeCand2( @@ -2210,7 +2212,9 @@ private module Stage3Param implements MkStage::StageParam { } } -private module Stage3 = MkStage::Stage; +private module Stage3 implements StageSig { + import MkStage::Stage +} /** * Holds if `argApf` is recorded as the summary context for flow reaching `node` From 792d34c3a14726965390603ba0ccf28d579f12b9 Mon Sep 17 00:00:00 2001 From: Tony Torralba Date: Fri, 5 Aug 2022 10:54:38 +0200 Subject: [PATCH 456/505] Add change note --- java/ql/lib/change-notes/2022-08-05-asynctask-improvements.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 java/ql/lib/change-notes/2022-08-05-asynctask-improvements.md diff --git a/java/ql/lib/change-notes/2022-08-05-asynctask-improvements.md b/java/ql/lib/change-notes/2022-08-05-asynctask-improvements.md new file mode 100644 index 00000000000..95c8438b324 --- /dev/null +++ b/java/ql/lib/change-notes/2022-08-05-asynctask-improvements.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Improved analysis of the Android class `AsyncTask` so that data can properly flow through its methods according to the life-cycle steps described here: https://developer.android.com/reference/android/os/AsyncTask#the-4-steps. \ No newline at end of file From 9ee90f8022c3fa2efd3050bc1d55959642d6632a Mon Sep 17 00:00:00 2001 From: Tony Torralba Date: Fri, 5 Aug 2022 11:11:13 +0200 Subject: [PATCH 457/505] Remove unnecessary import from test --- java/ql/test/library-tests/frameworks/android/asynctask/test.ql | 1 - 1 file changed, 1 deletion(-) diff --git a/java/ql/test/library-tests/frameworks/android/asynctask/test.ql b/java/ql/test/library-tests/frameworks/android/asynctask/test.ql index 58d90963fcb..5d91e4e8e26 100644 --- a/java/ql/test/library-tests/frameworks/android/asynctask/test.ql +++ b/java/ql/test/library-tests/frameworks/android/asynctask/test.ql @@ -1,3 +1,2 @@ import java import TestUtilities.InlineFlowTest -import semmle.code.java.dataflow.FlowSteps From 1ce06accbdf7cb55c9de3ace79a9e73fb5d5f482 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 5 Aug 2022 10:20:51 +0100 Subject: [PATCH 458/505] Swift: Fix capitalization issue? --- .../test/query-tests/Security/CWE-079/UnsafeWebViewFetch.qlref | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.qlref b/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.qlref index 57e842e89ff..a5c8cb457a0 100644 --- a/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.qlref +++ b/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.qlref @@ -1 +1 @@ -queries/Security/CWE-079/UnsafeWebviewFetch.ql \ No newline at end of file +queries/Security/CWE-079/UnsafeWebViewFetch.ql \ No newline at end of file From 5e69adb0a9682c00e3c2725af903d7c5cb6a2b08 Mon Sep 17 00:00:00 2001 From: Alex Denisov Date: Fri, 5 Aug 2022 11:49:52 +0200 Subject: [PATCH 459/505] Swift: extract comments --- swift/codegen/schema.yml | 4 ++++ swift/extractor/SwiftExtractor.cpp | 15 +++++++++++++++ swift/extractor/infra/SwiftDispatcher.h | 7 +++++++ swift/extractor/visitors/SwiftVisitor.h | 1 + swift/ql/lib/codeql/swift/elements.qll | 1 + swift/ql/lib/codeql/swift/elements/Comment.qll | 6 ++++++ swift/ql/lib/codeql/swift/generated/Comment.qll | 8 ++++++++ swift/ql/lib/swift.dbscheme | 6 ++++++ .../extractor-tests/comments/comments.expected | 4 ++++ .../ql/test/extractor-tests/comments/comments.ql | 4 ++++ .../test/extractor-tests/comments/comments.swift | 13 +++++++++++++ .../generated/Comment/MISSING_SOURCE.txt | 4 ++++ 12 files changed, 73 insertions(+) create mode 100644 swift/ql/lib/codeql/swift/elements/Comment.qll create mode 100644 swift/ql/lib/codeql/swift/generated/Comment.qll create mode 100644 swift/ql/test/extractor-tests/comments/comments.expected create mode 100644 swift/ql/test/extractor-tests/comments/comments.ql create mode 100644 swift/ql/test/extractor-tests/comments/comments.swift create mode 100644 swift/ql/test/extractor-tests/generated/Comment/MISSING_SOURCE.txt diff --git a/swift/codegen/schema.yml b/swift/codegen/schema.yml index 83e2c87ef4e..bdf90320d5f 100644 --- a/swift/codegen/schema.yml +++ b/swift/codegen/schema.yml @@ -34,6 +34,10 @@ Location: end_column: int _pragma: qltest_skip +Comment: + _extends: Locatable + text: string + Type: name: string canonical_type: Type diff --git a/swift/extractor/SwiftExtractor.cpp b/swift/extractor/SwiftExtractor.cpp index 22f4f01a470..f5a2fcb9e82 100644 --- a/swift/extractor/SwiftExtractor.cpp +++ b/swift/extractor/SwiftExtractor.cpp @@ -104,11 +104,26 @@ static void extractDeclarations(const SwiftExtractorConfiguration& config, trap.emit(unknownFileEntry); trap.emit(unknownLocationEntry); + std::vector comments; + if (primaryFile && primaryFile->getBufferID().hasValue()) { + auto& sourceManager = compiler.getSourceMgr(); + auto tokens = swift::tokenize(compiler.getInvocation().getLangOptions(), sourceManager, + primaryFile->getBufferID().getValue()); + for (auto& token : tokens) { + if (token.getKind() == swift::tok::comment) { + comments.push_back(token); + } + } + } + SwiftVisitor visitor(compiler.getSourceMgr(), trap, module, primaryFile); auto topLevelDecls = getTopLevelDecls(module, primaryFile); for (auto decl : topLevelDecls) { visitor.extract(decl); } + for (auto& comment : comments) { + visitor.extract(comment); + } } static std::unordered_set collectInputFilenames(swift::CompilerInstance& compiler) { diff --git a/swift/extractor/infra/SwiftDispatcher.h b/swift/extractor/infra/SwiftDispatcher.h index d564673c976..6ef53211630 100644 --- a/swift/extractor/infra/SwiftDispatcher.h +++ b/swift/extractor/infra/SwiftDispatcher.h @@ -3,6 +3,7 @@ #include #include #include +#include #include "swift/extractor/trap/TrapLabelStore.h" #include "swift/extractor/trap/TrapDomain.h" @@ -242,6 +243,12 @@ class SwiftDispatcher { return false; } + void emitComment(swift::Token& comment) { + CommentsTrap entry{trap.createLabel(), comment.getRawText().str()}; + trap.emit(entry); + attachLocation(comment.getRange().getStart(), comment.getRange().getEnd(), entry.id); + } + private: void attachLocation(swift::SourceLoc start, swift::SourceLoc end, diff --git a/swift/extractor/visitors/SwiftVisitor.h b/swift/extractor/visitors/SwiftVisitor.h index 965aa7975f3..f1828c39689 100644 --- a/swift/extractor/visitors/SwiftVisitor.h +++ b/swift/extractor/visitors/SwiftVisitor.h @@ -17,6 +17,7 @@ class SwiftVisitor : private SwiftDispatcher { void extract(const T& entity) { fetchLabel(entity); } + void extract(swift::Token& comment) { emitComment(comment); } private: void visit(swift::Decl* decl) override { declVisitor.visit(decl); } diff --git a/swift/ql/lib/codeql/swift/elements.qll b/swift/ql/lib/codeql/swift/elements.qll index 1a5da053693..7469e597a4b 100644 --- a/swift/ql/lib/codeql/swift/elements.qll +++ b/swift/ql/lib/codeql/swift/elements.qll @@ -1,6 +1,7 @@ // generated by codegen/codegen.py import codeql.swift.elements.AstNode import codeql.swift.elements.Callable +import codeql.swift.elements.Comment import codeql.swift.elements.Element import codeql.swift.elements.File import codeql.swift.elements.Locatable diff --git a/swift/ql/lib/codeql/swift/elements/Comment.qll b/swift/ql/lib/codeql/swift/elements/Comment.qll new file mode 100644 index 00000000000..c91ef59c845 --- /dev/null +++ b/swift/ql/lib/codeql/swift/elements/Comment.qll @@ -0,0 +1,6 @@ +private import codeql.swift.generated.Comment + +class Comment extends CommentBase { + /** toString */ + override string toString() { result = getText() } +} diff --git a/swift/ql/lib/codeql/swift/generated/Comment.qll b/swift/ql/lib/codeql/swift/generated/Comment.qll new file mode 100644 index 00000000000..ee6263126db --- /dev/null +++ b/swift/ql/lib/codeql/swift/generated/Comment.qll @@ -0,0 +1,8 @@ +// generated by codegen/codegen.py +import codeql.swift.elements.Locatable + +class CommentBase extends @comment, Locatable { + override string getAPrimaryQlClass() { result = "Comment" } + + string getText() { comments(this, result) } +} diff --git a/swift/ql/lib/swift.dbscheme b/swift/ql/lib/swift.dbscheme index 7c102d30524..b520efbade5 100644 --- a/swift/ql/lib/swift.dbscheme +++ b/swift/ql/lib/swift.dbscheme @@ -35,6 +35,7 @@ files( @locatable = @argument | @ast_node +| @comment | @condition_element | @if_config_clause ; @@ -54,6 +55,11 @@ locations( int end_column: int ref ); +comments( + unique int id: @comment, + string text: string ref +); + @type = @any_function_type | @any_generic_type diff --git a/swift/ql/test/extractor-tests/comments/comments.expected b/swift/ql/test/extractor-tests/comments/comments.expected new file mode 100644 index 00000000000..d6e262e2f60 --- /dev/null +++ b/swift/ql/test/extractor-tests/comments/comments.expected @@ -0,0 +1,4 @@ +| comments.swift:1:1:2:1 | // Single line comment\n | +| comments.swift:3:1:6:3 | /*\n Multiline\n comment\n*/ | +| comments.swift:8:1:9:1 | /// Single line doc comment\n | +| comments.swift:10:1:13:3 | /**\n Multiline\n doc comment\n*/ | diff --git a/swift/ql/test/extractor-tests/comments/comments.ql b/swift/ql/test/extractor-tests/comments/comments.ql new file mode 100644 index 00000000000..53e23810346 --- /dev/null +++ b/swift/ql/test/extractor-tests/comments/comments.ql @@ -0,0 +1,4 @@ +import swift + +from Comment c +select c diff --git a/swift/ql/test/extractor-tests/comments/comments.swift b/swift/ql/test/extractor-tests/comments/comments.swift new file mode 100644 index 00000000000..1be189ec459 --- /dev/null +++ b/swift/ql/test/extractor-tests/comments/comments.swift @@ -0,0 +1,13 @@ +// Single line comment + +/* + Multiline + comment +*/ + +/// Single line doc comment + +/** + Multiline + doc comment +*/ diff --git a/swift/ql/test/extractor-tests/generated/Comment/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/Comment/MISSING_SOURCE.txt new file mode 100644 index 00000000000..0d319d9a669 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/Comment/MISSING_SOURCE.txt @@ -0,0 +1,4 @@ +// generated by codegen/codegen.py + +After a swift source file is added in this directory and codegen/codegen.py is run again, test queries +will appear and this file will be deleted From 24c9ab80157a8ecee03723a6ff6872566e83d864 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Wed, 3 Aug 2022 17:47:33 +0100 Subject: [PATCH 460/505] Swift: Fix MaD for methods --- .../lib/codeql/swift/controlflow/CfgNodes.qll | 2 ++ .../internal/FlowSummaryImplSpecific.qll | 5 ++++- .../Security/CWE-079/UnsafeWebViewFetch.ql | 18 ------------------ 3 files changed, 6 insertions(+), 19 deletions(-) diff --git a/swift/ql/lib/codeql/swift/controlflow/CfgNodes.qll b/swift/ql/lib/codeql/swift/controlflow/CfgNodes.qll index 27af5b1c789..5358a35999f 100644 --- a/swift/ql/lib/codeql/swift/controlflow/CfgNodes.qll +++ b/swift/ql/lib/codeql/swift/controlflow/CfgNodes.qll @@ -125,6 +125,8 @@ class ApplyExprCfgNode extends ExprCfgNode { } AbstractFunctionDecl getStaticTarget() { result = e.getStaticTarget() } + + Expr getFunction() { result = e.getFunction() } } class CallExprCfgNode extends ApplyExprCfgNode { diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/FlowSummaryImplSpecific.qll b/swift/ql/lib/codeql/swift/dataflow/internal/FlowSummaryImplSpecific.qll index 5bb0aa9750e..79288e648e6 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/FlowSummaryImplSpecific.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/FlowSummaryImplSpecific.qll @@ -11,6 +11,7 @@ private import FlowSummaryImpl::Private private import FlowSummaryImpl::Public private import codeql.swift.dataflow.ExternalFlow private import codeql.swift.dataflow.FlowSummary as FlowSummary +private import codeql.swift.controlflow.CfgNodes class SummarizedCallableBase = AbstractFunctionDecl; @@ -153,7 +154,9 @@ class InterpretNode extends TInterpretNode { DataFlowCallable asCallable() { result.getUnderlyingCallable() = this.asElement() } /** Gets the target of this call, if any. */ - AbstractFunctionDecl getCallTarget() { result = this.asCall().asCall().getStaticTarget() } + AbstractFunctionDecl getCallTarget() { + result = this.asCall().asCall().getFunction().(ApplyExpr).getStaticTarget() + } /** Gets a textual representation of this node. */ string toString() { diff --git a/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql b/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql index 2f853db2975..b952a0c74c3 100644 --- a/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql +++ b/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql @@ -17,24 +17,6 @@ import codeql.swift.dataflow.DataFlow import codeql.swift.dataflow.TaintTracking import codeql.swift.dataflow.FlowSources import DataFlow::PathGraph -import codeql.swift.frameworks.StandardLibrary.String - -/** - * A taint source that is `String(contentsOf:)`. - * TODO: this shouldn't be needed when `StringSource` in `String.qll` is working. - */ -class StringContentsOfUrlSource extends RemoteFlowSource { - StringContentsOfUrlSource() { - exists(CallExpr call, AbstractFunctionDecl f | - call.getFunction().(ApplyExpr).getStaticTarget() = f and - f.getName() = "init(contentsOf:)" and - f.getParam(0).getType().getName() = "URL" and - this.asExpr() = call - ) - } - - override string getSourceType() { result = "" } -} /** * A sink that is a candidate result for this query, such as certain arguments From 946b8c68a6b1a14a4f73c8d49ad77f41e9a3e469 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 5 Aug 2022 11:19:00 +0100 Subject: [PATCH 461/505] Swift: Accept test changes. --- .../Security/CWE-079/UnsafeWebViewFetch.expected | 6 ++++++ .../query-tests/Security/CWE-079/UnsafeWebViewFetch.swift | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.expected b/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.expected index d387127ac4e..53983de94c5 100644 --- a/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.expected +++ b/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.expected @@ -7,6 +7,7 @@ edges | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:94:10:94:37 | try ... : | | UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:121:25:121:25 | remoteString | | UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:124:25:124:51 | ... call to +(_:_:) ... | +| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:127:25:127:25 | "..." | | UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:135:25:135:25 | remoteString | | UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:137:25:137:25 | remoteString | | UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:138:47:138:56 | ...! | @@ -19,6 +20,7 @@ edges | UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:154:86:154:95 | ...! | | UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:168:25:168:25 | remoteString | | UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:171:25:171:51 | ... call to +(_:_:) ... | +| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:174:25:174:25 | "..." | | UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:182:25:182:25 | remoteString | | UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:184:25:184:25 | remoteString | | UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:185:47:185:56 | ...! | @@ -38,6 +40,7 @@ nodes | UnsafeWebViewFetch.swift:120:25:120:39 | call to getRemoteData() | semmle.label | call to getRemoteData() | | UnsafeWebViewFetch.swift:121:25:121:25 | remoteString | semmle.label | remoteString | | UnsafeWebViewFetch.swift:124:25:124:51 | ... call to +(_:_:) ... | semmle.label | ... call to +(_:_:) ... | +| UnsafeWebViewFetch.swift:127:25:127:25 | "..." | semmle.label | "..." | | UnsafeWebViewFetch.swift:135:25:135:25 | remoteString | semmle.label | remoteString | | UnsafeWebViewFetch.swift:137:25:137:25 | remoteString | semmle.label | remoteString | | UnsafeWebViewFetch.swift:138:47:138:56 | ...! | semmle.label | ...! | @@ -52,6 +55,7 @@ nodes | UnsafeWebViewFetch.swift:167:25:167:39 | call to getRemoteData() | semmle.label | call to getRemoteData() | | UnsafeWebViewFetch.swift:168:25:168:25 | remoteString | semmle.label | remoteString | | UnsafeWebViewFetch.swift:171:25:171:51 | ... call to +(_:_:) ... | semmle.label | ... call to +(_:_:) ... | +| UnsafeWebViewFetch.swift:174:25:174:25 | "..." | semmle.label | "..." | | UnsafeWebViewFetch.swift:182:25:182:25 | remoteString | semmle.label | remoteString | | UnsafeWebViewFetch.swift:184:25:184:25 | remoteString | semmle.label | remoteString | | UnsafeWebViewFetch.swift:185:47:185:56 | ...! | semmle.label | ...! | @@ -70,11 +74,13 @@ subpaths | UnsafeWebViewFetch.swift:120:25:120:39 | call to getRemoteData() | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:120:25:120:39 | call to getRemoteData() | Tainted data is used in a WebView fetch without restricting the base URL. | | UnsafeWebViewFetch.swift:121:25:121:25 | remoteString | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:121:25:121:25 | remoteString | Tainted data is used in a WebView fetch without restricting the base URL. | | UnsafeWebViewFetch.swift:124:25:124:51 | ... call to +(_:_:) ... | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:124:25:124:51 | ... call to +(_:_:) ... | Tainted data is used in a WebView fetch without restricting the base URL. | +| UnsafeWebViewFetch.swift:127:25:127:25 | "..." | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:127:25:127:25 | "..." | Tainted data is used in a WebView fetch without restricting the base URL. | | UnsafeWebViewFetch.swift:139:25:139:25 | remoteString | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:139:25:139:25 | remoteString | Tainted data is used in a WebView fetch with a tainted base URL. | | UnsafeWebViewFetch.swift:141:25:141:25 | remoteString | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:141:25:141:25 | remoteString | Tainted data is used in a WebView fetch with a tainted base URL. | | UnsafeWebViewFetch.swift:167:25:167:39 | call to getRemoteData() | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:167:25:167:39 | call to getRemoteData() | Tainted data is used in a WebView fetch without restricting the base URL. | | UnsafeWebViewFetch.swift:168:25:168:25 | remoteString | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:168:25:168:25 | remoteString | Tainted data is used in a WebView fetch without restricting the base URL. | | UnsafeWebViewFetch.swift:171:25:171:51 | ... call to +(_:_:) ... | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:171:25:171:51 | ... call to +(_:_:) ... | Tainted data is used in a WebView fetch without restricting the base URL. | +| UnsafeWebViewFetch.swift:174:25:174:25 | "..." | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:174:25:174:25 | "..." | Tainted data is used in a WebView fetch without restricting the base URL. | | UnsafeWebViewFetch.swift:186:25:186:25 | remoteString | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:186:25:186:25 | remoteString | Tainted data is used in a WebView fetch with a tainted base URL. | | UnsafeWebViewFetch.swift:188:25:188:25 | remoteString | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:188:25:188:25 | remoteString | Tainted data is used in a WebView fetch with a tainted base URL. | | UnsafeWebViewFetch.swift:210:25:210:25 | htmlData | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:210:25:210:25 | htmlData | Tainted data is used in a WebView fetch without restricting the base URL. | diff --git a/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.swift b/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.swift index 9dcef0262ec..c6fc1628fbe 100644 --- a/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.swift +++ b/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.swift @@ -124,7 +124,7 @@ func testUIWebView() { webview.loadHTMLString("" + remoteString + "", baseURL: nil) // BAD webview.loadHTMLString("\(localStringFragment)", baseURL: nil) // GOOD: the HTML data is local - webview.loadHTMLString("\(remoteString)", baseURL: nil) // BAD [NOT DETECTED] + webview.loadHTMLString("\(remoteString)", baseURL: nil) // BAD let localSafeURL = URL(string: "about:blank") let localURL = URL(string: "http://example.com/") @@ -171,7 +171,7 @@ func testWKWebView() { webview.loadHTMLString("" + remoteString + "", baseURL: nil) // BAD webview.loadHTMLString("\(localStringFragment)", baseURL: nil) // GOOD: the HTML data is local - webview.loadHTMLString("\(remoteString)", baseURL: nil) // BAD [NOT DETECTED] + webview.loadHTMLString("\(remoteString)", baseURL: nil) // BAD let localSafeURL = URL(string: "about:blank") let localURL = URL(string: "http://example.com/") From b75b073daef33d7dc7eb1002b866afad897e7617 Mon Sep 17 00:00:00 2001 From: Tony Torralba Date: Fri, 5 Aug 2022 12:21:22 +0200 Subject: [PATCH 462/505] Remove unused class member --- java/ql/lib/semmle/code/java/frameworks/android/AsyncTask.qll | 2 -- 1 file changed, 2 deletions(-) diff --git a/java/ql/lib/semmle/code/java/frameworks/android/AsyncTask.qll b/java/ql/lib/semmle/code/java/frameworks/android/AsyncTask.qll index 0b0334a0664..1e739bcba2b 100644 --- a/java/ql/lib/semmle/code/java/frameworks/android/AsyncTask.qll +++ b/java/ql/lib/semmle/code/java/frameworks/android/AsyncTask.qll @@ -77,8 +77,6 @@ private class AsyncTaskInit extends Callable { /** A call to the `execute` or `executeOnExecutor` methods of the `android.os.AsyncTask` class. */ private class ExecuteAsyncTaskMethodAccess extends MethodAccess { - Argument paramsArgument; - ExecuteAsyncTaskMethodAccess() { this.getMethod().hasName(["execute", "executeOnExecutor"]) and this.getMethod().getDeclaringType().getSourceDeclaration().getASourceSupertype*() instanceof From 69564d2192b0a984b0bdd35ee4c4cb2273b6835c Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 5 Aug 2022 11:48:29 +0100 Subject: [PATCH 463/505] Swift: Add a couple of standard Comment subclasses. --- .../ql/lib/codeql/swift/elements/Comment.qll | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/swift/ql/lib/codeql/swift/elements/Comment.qll b/swift/ql/lib/codeql/swift/elements/Comment.qll index c91ef59c845..c0fb2089e3b 100644 --- a/swift/ql/lib/codeql/swift/elements/Comment.qll +++ b/swift/ql/lib/codeql/swift/elements/Comment.qll @@ -4,3 +4,32 @@ class Comment extends CommentBase { /** toString */ override string toString() { result = getText() } } + +class SingleLineComment extends Comment { + SingleLineComment() { + this.getText().matches("//%") and + not this instanceof SingleLineDocComment + } +} + +class MultiLineComment extends Comment { + MultiLineComment() { + this.getText().matches("/*%") and + not this instanceof MultiLineDocComment + } +} + +class DocComment extends Comment { + DocComment() { + this instanceof SingleLineDocComment or + this instanceof MultiLineDocComment + } +} + +class SingleLineDocComment extends Comment { + SingleLineDocComment() { this.getText().matches("///%") } +} + +class MultiLineDocComment extends Comment { + MultiLineDocComment() { this.getText().matches("/**%") } +} From 46ec7a9b826441c39202252689a2f63876fef8be Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 5 Aug 2022 11:49:15 +0100 Subject: [PATCH 464/505] Swift: Add the InlineExpectationsTest framework. --- .../TestUtilities/InlineExpectationsTest.qll | 377 ++++++++++++++++++ .../InlineExpectationsTestPrivate.qll | 23 ++ 2 files changed, 400 insertions(+) create mode 100644 swift/ql/test/TestUtilities/InlineExpectationsTest.qll create mode 100644 swift/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll diff --git a/swift/ql/test/TestUtilities/InlineExpectationsTest.qll b/swift/ql/test/TestUtilities/InlineExpectationsTest.qll new file mode 100644 index 00000000000..4b4a31d6950 --- /dev/null +++ b/swift/ql/test/TestUtilities/InlineExpectationsTest.qll @@ -0,0 +1,377 @@ +/** + * Provides a library for writing QL tests whose success or failure is based on expected results + * embedded in the test source code as comments, rather than the contents of an `.expected` file + * (in that the `.expected` file should always be empty). + * + * To add this framework to a new language: + * - Add a file `InlineExpectationsTestPrivate.qll` that defines a `ExpectationComment` class. This class + * must support a `getContents` method that returns the contents of the given comment, _excluding_ + * the comment indicator itself. It should also define `toString` and `getLocation` as usual. + * + * To create a new inline expectations test: + * - Declare a class that extends `InlineExpectationsTest`. In the characteristic predicate of the + * new class, bind `this` to a unique string (usually the name of the test). + * - Override the `hasActualResult()` predicate to produce the actual results of the query. For each + * result, specify a `Location`, a text description of the element for which the result was + * reported, a short string to serve as the tag to identify expected results for this test, and the + * expected value of the result. + * - Override `getARelevantTag()` to return the set of tags that can be produced by + * `hasActualResult()`. Often this is just a single tag. + * + * Example: + * ```ql + * class ConstantValueTest extends InlineExpectationsTest { + * ConstantValueTest() { this = "ConstantValueTest" } + * + * override string getARelevantTag() { + * // We only use one tag for this test. + * result = "const" + * } + * + * override predicate hasActualResult( + * Location location, string element, string tag, string value + * ) { + * exists(Expr e | + * tag = "const" and // The tag for this test. + * value = e.getValue() and // The expected value. Will only hold for constant expressions. + * location = e.getLocation() and // The location of the result to be reported. + * element = e.toString() // The display text for the result. + * ) + * } + * } + * ``` + * + * There is no need to write a `select` clause or query predicate. All of the differences between + * expected results and actual results will be reported in the `failures()` query predicate. + * + * To annotate the test source code with an expected result, place a comment starting with a `$` on the + * same line as the expected result, with text of the following format as the body of the comment: + * + * `tag=expected-value` + * + * Where `tag` is the value of the `tag` parameter from `hasActualResult()`, and `expected-value` is + * the value of the `value` parameter from `hasActualResult()`. The `=expected-value` portion may be + * omitted, in which case `expected-value` is treated as the empty string. Multiple expectations may + * be placed in the same comment. Any actual result that + * appears on a line that does not contain a matching expected result comment will be reported with + * a message of the form "Unexpected result: tag=value". Any expected result comment for which there + * is no matching actual result will be reported with a message of the form + * "Missing result: tag=expected-value". + * + * Example: + * ```cpp + * int i = x + 5; // $ const=5 + * int j = y + (7 - 3) // $ const=7 const=3 const=4 // The result of the subtraction is a constant. + * ``` + * + * For tests that contain known missing and spurious results, it is possible to further + * annotate that a particular expected result is known to be spurious, or that a particular + * missing result is known to be missing: + * + * `$ SPURIOUS: tag=expected-value` // Spurious result + * `$ MISSING: tag=expected-value` // Missing result + * + * A spurious expectation is treated as any other expected result, except that if there is no + * matching actual result, the message will be of the form "Fixed spurious result: tag=value". A + * missing expectation is treated as if there were no expected result, except that if a + * matching expected result is found, the message will be of the form + * "Fixed missing result: tag=value". + * + * A single line can contain all the expected, spurious and missing results of that line. For instance: + * `$ tag1=value1 SPURIOUS: tag2=value2 MISSING: tag3=value3`. + * + * If the same result value is expected for two or more tags on the same line, there is a shorthand + * notation available: + * + * `tag1,tag2=expected-value` + * + * is equivalent to: + * + * `tag1=expected-value tag2=expected-value` + */ + +private import InlineExpectationsTestPrivate + +/** + * The base class for tests with inline expectations. The test extends this class to provide the actual + * results of the query, which are then compared with the expected results in comments to produce a + * list of failure messages that point out where the actual results differ from the expected + * results. + */ +abstract class InlineExpectationsTest extends string { + bindingset[this] + InlineExpectationsTest() { any() } + + /** + * Returns all tags that can be generated by this test. Most tests will only ever produce a single + * tag. Any expected result comments for a tag that is not returned by the `getARelevantTag()` + * predicate for an active test will be ignored. This makes it possible to write multiple tests in + * different `.ql` files that all query the same source code. + */ + abstract string getARelevantTag(); + + /** + * Returns the actual results of the query that is being tested. Each result consist of the + * following values: + * - `location` - The source code location of the result. Any expected result comment must appear + * on the start line of this location. + * - `element` - Display text for the element on which the result is reported. + * - `tag` - The tag that marks this result as coming from this test. This must be one of the tags + * returned by `getARelevantTag()`. + * - `value` - The value of the result, which will be matched against the value associated with + * `tag` in any expected result comment on that line. + */ + abstract predicate hasActualResult(Location location, string element, string tag, string value); + + /** + * Holds if there is an optional result on the specified location. + * + * This is similar to `hasActualResult`, but returns results that do not require a matching annotation. + * A failure will still arise if there is an annotation that does not match any results, but not vice versa. + * Override this predicate to specify optional results. + */ + predicate hasOptionalResult(Location location, string element, string tag, string value) { + none() + } + + final predicate hasFailureMessage(FailureLocatable element, string message) { + exists(ActualResult actualResult | + actualResult.getTest() = this and + element = actualResult and + ( + exists(FalseNegativeExpectation falseNegative | + falseNegative.matchesActualResult(actualResult) and + message = "Fixed missing result:" + falseNegative.getExpectationText() + ) + or + not exists(ValidExpectation expectation | expectation.matchesActualResult(actualResult)) and + message = "Unexpected result: " + actualResult.getExpectationText() and + not actualResult.isOptional() + ) + ) + or + exists(ValidExpectation expectation | + not exists(ActualResult actualResult | expectation.matchesActualResult(actualResult)) and + expectation.getTag() = getARelevantTag() and + element = expectation and + ( + expectation instanceof GoodExpectation and + message = "Missing result:" + expectation.getExpectationText() + or + expectation instanceof FalsePositiveExpectation and + message = "Fixed spurious result:" + expectation.getExpectationText() + ) + ) + or + exists(InvalidExpectation expectation | + element = expectation and + message = "Invalid expectation syntax: " + expectation.getExpectation() + ) + } +} + +/** + * RegEx pattern to match a comment containing one or more expected results. The comment must have + * `$` as its first non-whitespace character. Any subsequent character + * is treated as part of the expected results, except that the comment may contain a `//` sequence + * to treat the remainder of the line as a regular (non-interpreted) comment. + */ +private string expectationCommentPattern() { result = "\\s*\\$((?:[^/]|/[^/])*)(?://.*)?" } + +/** + * The possible columns in an expectation comment. The `TDefaultColumn` branch represents the first + * column in a comment. This column is not precedeeded by a name. `TNamedColumn(name)` represents a + * column containing expected results preceded by the string `name:`. + */ +private newtype TColumn = + TDefaultColumn() or + TNamedColumn(string name) { name = ["MISSING", "SPURIOUS"] } + +bindingset[start, content] +private int getEndOfColumnPosition(int start, string content) { + result = + min(string name, int cand | + exists(TNamedColumn(name)) and + cand = content.indexOf(name + ":") and + cand >= start + | + cand + ) + or + not exists(string name | + exists(TNamedColumn(name)) and + content.indexOf(name + ":") >= start + ) and + result = content.length() +} + +private predicate getAnExpectation( + ExpectationComment comment, TColumn column, string expectation, string tags, string value +) { + exists(string content | + content = comment.getContents().regexpCapture(expectationCommentPattern(), 1) and + ( + column = TDefaultColumn() and + exists(int end | + end = getEndOfColumnPosition(0, content) and + expectation = content.prefix(end).regexpFind(expectationPattern(), _, _).trim() + ) + or + exists(string name, int start, int end | + column = TNamedColumn(name) and + start = content.indexOf(name + ":") + name.length() + 1 and + end = getEndOfColumnPosition(start, content) and + expectation = content.substring(start, end).regexpFind(expectationPattern(), _, _).trim() + ) + ) + ) and + tags = expectation.regexpCapture(expectationPattern(), 1) and + if exists(expectation.regexpCapture(expectationPattern(), 2)) + then value = expectation.regexpCapture(expectationPattern(), 2) + else value = "" +} + +private string getColumnString(TColumn column) { + column = TDefaultColumn() and result = "" + or + column = TNamedColumn(result) +} + +/** + * RegEx pattern to match a single expected result, not including the leading `$`. It consists of one or + * more comma-separated tags optionally followed by `=` and the expected value. + * + * Tags must be only letters, digits, `-` and `_` (note that the first character + * must not be a digit), but can contain anything enclosed in a single set of + * square brackets. + * + * Examples: + * - `tag` + * - `tag=value` + * - `tag,tag2=value` + * - `tag[foo bar]=value` + * + * Not allowed: + * - `tag[[[foo bar]` + */ +private string expectationPattern() { + exists(string tag, string tags, string value | + tag = "[A-Za-z-_](?:[A-Za-z-_0-9]|\\[[^\\]\\]]*\\])*" and + tags = "((?:" + tag + ")(?:\\s*,\\s*" + tag + ")*)" and + // In Python, we allow both `"` and `'` for strings, as well as the prefixes `bru`. + // For example, `b"foo"`. + value = "((?:[bru]*\"[^\"]*\"|[bru]*'[^']*'|\\S+)*)" and + result = tags + "(?:=" + value + ")?" + ) +} + +private newtype TFailureLocatable = + TActualResult( + InlineExpectationsTest test, Location location, string element, string tag, string value, + boolean optional + ) { + test.hasActualResult(location, element, tag, value) and + optional = false + or + test.hasOptionalResult(location, element, tag, value) and optional = true + } or + TValidExpectation(ExpectationComment comment, string tag, string value, string knownFailure) { + exists(TColumn column, string tags | + getAnExpectation(comment, column, _, tags, value) and + tag = tags.splitAt(",") and + knownFailure = getColumnString(column) + ) + } or + TInvalidExpectation(ExpectationComment comment, string expectation) { + getAnExpectation(comment, _, expectation, _, _) and + not expectation.regexpMatch(expectationPattern()) + } + +class FailureLocatable extends TFailureLocatable { + string toString() { none() } + + Location getLocation() { none() } + + final string getExpectationText() { result = getTag() + "=" + getValue() } + + string getTag() { none() } + + string getValue() { none() } +} + +class ActualResult extends FailureLocatable, TActualResult { + InlineExpectationsTest test; + Location location; + string element; + string tag; + string value; + boolean optional; + + ActualResult() { this = TActualResult(test, location, element, tag, value, optional) } + + override string toString() { result = element } + + override Location getLocation() { result = location } + + InlineExpectationsTest getTest() { result = test } + + override string getTag() { result = tag } + + override string getValue() { result = value } + + predicate isOptional() { optional = true } +} + +abstract private class Expectation extends FailureLocatable { + ExpectationComment comment; + + override string toString() { result = comment.toString() } + + override Location getLocation() { result = comment.getLocation() } +} + +private class ValidExpectation extends Expectation, TValidExpectation { + string tag; + string value; + string knownFailure; + + ValidExpectation() { this = TValidExpectation(comment, tag, value, knownFailure) } + + override string getTag() { result = tag } + + override string getValue() { result = value } + + string getKnownFailure() { result = knownFailure } + + predicate matchesActualResult(ActualResult actualResult) { + getLocation().getStartLine() = actualResult.getLocation().getStartLine() and + getLocation().getFile() = actualResult.getLocation().getFile() and + getTag() = actualResult.getTag() and + getValue() = actualResult.getValue() + } +} + +/* Note: These next three classes correspond to all the possible values of type `TColumn`. */ +class GoodExpectation extends ValidExpectation { + GoodExpectation() { getKnownFailure() = "" } +} + +class FalsePositiveExpectation extends ValidExpectation { + FalsePositiveExpectation() { getKnownFailure() = "SPURIOUS" } +} + +class FalseNegativeExpectation extends ValidExpectation { + FalseNegativeExpectation() { getKnownFailure() = "MISSING" } +} + +class InvalidExpectation extends Expectation, TInvalidExpectation { + string expectation; + + InvalidExpectation() { this = TInvalidExpectation(comment, expectation) } + + string getExpectation() { result = expectation } +} + +query predicate failures(FailureLocatable element, string message) { + exists(InlineExpectationsTest test | test.hasFailureMessage(element, message)) +} diff --git a/swift/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll b/swift/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll new file mode 100644 index 00000000000..1776f04aea4 --- /dev/null +++ b/swift/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll @@ -0,0 +1,23 @@ +import swift + +private newtype TExpectationComment = MkExpectationComment(SingleLineComment c) + +/** + * Represents a line comment. + * Unlike the `SingleLineComment` class, however, the string returned by `getContents` does _not_ + * include the preceding comment marker (`//`). + */ +class ExpectationComment extends TExpectationComment { + SingleLineComment comment; + + ExpectationComment() { this = MkExpectationComment(comment) } + + /** Returns the contents of the given comment, _without_ the preceding comment marker (`//`). */ + string getContents() { result = comment.getText().suffix(2) } + + /** Gets a textual representation of this element. */ + string toString() { result = comment.toString() } + + /** Gets the location of this comment. */ + Location getLocation() { result = comment.getLocation() } +} From b20b0a091d47e13059393ab36bdbd1b178e46107 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 5 Aug 2022 11:49:36 +0100 Subject: [PATCH 465/505] Update identical-files. --- config/identical-files.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config/identical-files.json b/config/identical-files.json index 990ba49f033..403e6b91c2f 100644 --- a/config/identical-files.json +++ b/config/identical-files.json @@ -392,7 +392,8 @@ "python/ql/test/TestUtilities/InlineExpectationsTest.qll", "ruby/ql/test/TestUtilities/InlineExpectationsTest.qll", "ql/ql/test/TestUtilities/InlineExpectationsTest.qll", - "go/ql/test/TestUtilities/InlineExpectationsTest.qll" + "go/ql/test/TestUtilities/InlineExpectationsTest.qll", + "swift/ql/test/TestUtilities/InlineExpectationsTest.qll" ], "C++ ExternalAPIs": [ "cpp/ql/src/Security/CWE/CWE-020/ExternalAPIs.qll", From 16e16f08dcb8ccf7923e8daa12108e0cb0303833 Mon Sep 17 00:00:00 2001 From: Joe Farebrother Date: Fri, 17 Jun 2022 14:14:58 +0100 Subject: [PATCH 466/505] Add webview cert validation query --- ...droidWebViewCertificateValidationQuery.qll | 26 +++++++++++++++++++ .../ImproperWebViewCertificateValidation.ql | 18 +++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 java/ql/lib/semmle/code/java/security/AndroidWebViewCertificateValidationQuery.qll create mode 100644 java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.ql diff --git a/java/ql/lib/semmle/code/java/security/AndroidWebViewCertificateValidationQuery.qll b/java/ql/lib/semmle/code/java/security/AndroidWebViewCertificateValidationQuery.qll new file mode 100644 index 00000000000..5b459f07fac --- /dev/null +++ b/java/ql/lib/semmle/code/java/security/AndroidWebViewCertificateValidationQuery.qll @@ -0,0 +1,26 @@ +import java + +class OnReceivedSslErrorMethod extends Method { + OnReceivedSslErrorMethod() { + this.hasQualifiedName("android.webkit", "WebViewClient", "onReceivedSslError") + } + + Parameter handlerArg() { result = this.getParameter(1) } +} + +private class SslCancelCall extends MethodAccess { + SslCancelCall() { + this.getMethod().hasQualifiedName("android.webkit", "SslErrorHandler", "cancel") + } +} + +private class SslProceedCall extends MethodAccess { + SslProceedCall() { + this.getMethod().hasQualifiedName("android.webkit", "SslErrorHandler", "proceed") + } +} + +predicate trustsAllCerts(OnReceivedSslErrorMethod m) { + exists(SslProceedCall pr | pr.getQualifier().(VarAccess).getVariable() = m.handlerArg()) and + not exists(SslCancelCall ca | ca.getQualifier().(VarAccess).getVariable() = m.handlerArg()) +} diff --git a/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.ql b/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.ql new file mode 100644 index 00000000000..5d2f4cf7eb9 --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.ql @@ -0,0 +1,18 @@ +/** + * @name Android `WebVeiw` that accepts all certificates + * @description Trusting all certificates allows an attacker to perform a machine-in-the-middle attack. + * @kind problem + * @problem.severity error + * @security-severity 7.5 + * @precision high + * @id java/improper-webview-certificate-validation + * @tags security + * external/cwe/cwe-295 + */ + +import java +import semmle.code.java.security.AndroidWebViewCertificateValidationQuery + +from OnReceivedSslErrorMethod m +where trustsAllCerts(m) +select m, "This handler accepts all SSL certificates." From c4de158e0d83b17422ef99b567a38f3f84254faa Mon Sep 17 00:00:00 2001 From: Joe Farebrother Date: Mon, 20 Jun 2022 14:54:21 +0100 Subject: [PATCH 467/505] Add tests --- .../Test.java | 54 +++++++++++++++++++ .../options | 1 + .../test.expected | 0 .../test.ql | 19 +++++++ 4 files changed, 74 insertions(+) create mode 100644 java/ql/test/query-tests/security/CWE-295/ImproperWebVeiwCertificateValidation/Test.java create mode 100644 java/ql/test/query-tests/security/CWE-295/ImproperWebVeiwCertificateValidation/options create mode 100644 java/ql/test/query-tests/security/CWE-295/ImproperWebVeiwCertificateValidation/test.expected create mode 100644 java/ql/test/query-tests/security/CWE-295/ImproperWebVeiwCertificateValidation/test.ql diff --git a/java/ql/test/query-tests/security/CWE-295/ImproperWebVeiwCertificateValidation/Test.java b/java/ql/test/query-tests/security/CWE-295/ImproperWebVeiwCertificateValidation/Test.java new file mode 100644 index 00000000000..3ddb0c6aad5 --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-295/ImproperWebVeiwCertificateValidation/Test.java @@ -0,0 +1,54 @@ +import android.webkit.WebViewClient; +import android.webkit.WebView; +import android.webkit.SslErrorHandler; +import android.net.http.SslError; +import android.net.http.SslCertificate; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.app.Activity; + +class Test { + class A extends WebViewClient { + public void onReceivedSslError (WebView view, SslErrorHandler handler, SslError error) { + handler.proceed(); // $hasResult + } + } + + interface Validator { + boolean isValid(SslCertificate cert); + } + + class B extends WebViewClient { + Validator v; + + public void onReceivedSslError (WebView view, SslErrorHandler handler, SslError error) { + if (this.v.isValid(error.getCertificate())) { + handler.proceed(); + } + else { + handler.cancel(); + } + } + } + + class C extends WebViewClient { + Activity activity; + + public void onReceivedSslError (WebView view, SslErrorHandler handler, SslError error) { + new AlertDialog.Builder(activity). + setTitle("SSL error"). + setMessage("SSL error. Connect anyway?"). + setPositiveButton("Yes", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + handler.proceed(); + } + }).setNegativeButton("No", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + handler.cancel(); + } + }).show(); + } + } +} \ No newline at end of file diff --git a/java/ql/test/query-tests/security/CWE-295/ImproperWebVeiwCertificateValidation/options b/java/ql/test/query-tests/security/CWE-295/ImproperWebVeiwCertificateValidation/options new file mode 100644 index 00000000000..a36a7e0c5ee --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-295/ImproperWebVeiwCertificateValidation/options @@ -0,0 +1 @@ +// semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/google-android-9.0.0 \ No newline at end of file diff --git a/java/ql/test/query-tests/security/CWE-295/ImproperWebVeiwCertificateValidation/test.expected b/java/ql/test/query-tests/security/CWE-295/ImproperWebVeiwCertificateValidation/test.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/java/ql/test/query-tests/security/CWE-295/ImproperWebVeiwCertificateValidation/test.ql b/java/ql/test/query-tests/security/CWE-295/ImproperWebVeiwCertificateValidation/test.ql new file mode 100644 index 00000000000..6166e0fe239 --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-295/ImproperWebVeiwCertificateValidation/test.ql @@ -0,0 +1,19 @@ +import java +import semmle.code.java.security.AndroidWebViewCertificateValidationQuery +import TestUtilities.InlineExpectationsTest + +class WebViewTest extends InlineExpectationsTest { + WebViewTest() { this = "WebViewTest" } + + override string getARelevantTag() { result = "hasResult" } + + override predicate hasActualResult(Location location, string element, string tag, string value) { + exists(OnReceivedSslErrorMethod m | + trustsAllCerts(m) and + location = m.getLocation() and + element = m.toString() and + tag = "hasResult" and + value = "" + ) + } +} From 498ad230c293ee1827ffc0653ad4c123940b751b Mon Sep 17 00:00:00 2001 From: Joe Farebrother Date: Tue, 21 Jun 2022 16:12:05 +0100 Subject: [PATCH 468/505] Update stubs --- .../android/app/ActionBar.java | 132 ++ .../android/app/ActivityManager.java | 215 +++ .../android/app/AlertDialog.java | 95 ++ .../android/app/Application.java | 55 + .../android/app/ApplicationExitInfo.java | 47 + .../android/app/Dialog.java | 121 ++ .../android/app/DirectAction.java | 20 + .../android/app/Fragment.java | 204 ++- .../android/app/FragmentManager.java | 75 + .../android/app/FragmentTransaction.java | 48 + .../android/app/LoaderManager.java | 25 + .../android/app/PictureInPictureParams.java | 16 + .../android/app/PictureInPictureUiState.java | 16 + .../android/app/RemoteAction.java | 30 + .../android/app/SharedElementCallback.java | 27 + .../android/app/TaskInfo.java | 21 + .../android/app/TaskStackBuilder.java | 28 + .../android/app/VoiceInteractor.java | 31 + .../android/app/assist/AssistContent.java | 29 + .../android/content/ComponentCallbacks2.java | 6 +- .../android/content/DialogInterface.java | 41 + .../android/content/Loader.java | 51 + .../android/media/AudioAttributes.java | 4 + .../android/net/http/SslCertificate.java | 34 + .../android/net/http/SslError.java | 28 + .../android/os/Debug.java | 110 ++ .../android/os/Parcelable.java | 5 + .../android/print/PageRange.java | 21 + .../android/print/PrintAttributes.java | 162 ++ .../android/print/PrintDocumentAdapter.java | 32 + .../android/print/PrintDocumentInfo.java | 25 + .../android/text/Spannable.java | 6 + .../android/transition/PathMotion.java | 14 + .../android/transition/Scene.java | 22 + .../android/transition/Transition.java | 83 + .../android/transition/TransitionManager.java | 20 + .../transition/TransitionPropagation.java | 15 + .../android/transition/TransitionValues.java | 17 + .../android/view/AttachedSurfaceControl.java | 7 + .../android/view/DragAndDropPermissions.java | 15 + .../android/view/FrameMetrics.java | 25 + .../android/view/InputQueue.java | 14 + .../android/view/KeyboardShortcutGroup.java | 21 + .../android/view/KeyboardShortcutInfo.java | 20 + .../android/view/SearchEvent.java | 12 + .../android/view/SurfaceControl.java | 6 + .../android/view/SurfaceHolder.java | 40 + .../android/view/View.java | 1 + .../android/view/ViewGroup.java | 21 + .../android/view/Window.java | 251 +++ .../android/view/WindowManager.java | 183 ++ .../android/view/WindowMetrics.java | 14 + .../accessibility/AccessibilityEvent.java | 3 + .../accessibility/AccessibilityNodeInfo.java | 3 + .../textclassifier/ConversationAction.java | 31 + .../textclassifier/ConversationActions.java | 51 + .../view/textclassifier/SelectionEvent.java | 61 + .../textclassifier/TextClassification.java | 48 + .../TextClassificationContext.java | 18 + .../TextClassificationSessionId.java | 17 + .../view/textclassifier/TextClassifier.java | 68 + .../textclassifier/TextClassifierEvent.java | 54 + .../view/textclassifier/TextLanguage.java | 32 + .../view/textclassifier/TextLinks.java | 16 + .../view/textclassifier/TextSelection.java | 40 + .../android/webkit/ClientCertRequest.java | 19 + .../android/webkit/ConsoleMessage.java | 19 + .../android/webkit/DownloadListener.java | 9 + .../webkit/GeolocationPermissions.java | 20 + .../android/webkit/HttpAuthHandler.java | 12 + .../android/webkit/JsPromptResult.java | 10 + .../android/webkit/JsResult.java | 10 + .../android/webkit/PermissionRequest.java | 18 + .../webkit/RenderProcessGoneDetail.java | 11 + .../android/webkit/SafeBrowsingResponse.java | 12 + .../android/webkit/SslErrorHandler.java | 11 + .../android/webkit/ValueCallback.java | 9 + .../android/webkit/WebBackForwardList.java | 16 + .../android/webkit/WebChromeClient.java | 67 + .../android/webkit/WebHistoryItem.java | 15 + .../android/webkit/WebMessage.java | 14 + .../android/webkit/WebMessagePort.java | 19 + .../android/webkit/WebResourceError.java | 10 + .../android/webkit/WebResourceRequest.java | 76 +- .../android/webkit/WebResourceResponse.java | 187 +- .../android/webkit/WebSettings.java | 1507 ++--------------- .../android/webkit/WebStorage.java | 21 + .../android/webkit/WebView.java | 388 +++-- .../android/webkit/WebViewClient.java | 289 +--- .../android/webkit/WebViewRenderProcess.java | 10 + .../webkit/WebViewRenderProcessClient.java | 13 + .../android/widget/AbsListView.java | 212 +++ .../android/widget/AbsoluteLayout.java | 23 + .../android/widget/Adapter.java | 24 + .../android/widget/AdapterView.java | 77 + .../android/widget/Button.java | 48 +- .../android/widget/Filter.java | 24 + .../android/widget/FrameLayout.java | 40 + .../android/widget/ListAdapter.java | 11 + .../android/widget/ListView.java | 73 + .../android/widget/SpinnerAdapter.java | 12 + .../android/widget/TextView.java | 1169 ++++--------- .../android/widget/Toolbar.java | 116 ++ .../android/window/SplashScreen.java | 16 + .../android/window/SplashScreenView.java | 18 + 105 files changed, 4782 insertions(+), 2876 deletions(-) create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/app/ActionBar.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/app/ActivityManager.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/app/AlertDialog.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/app/Application.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/app/ApplicationExitInfo.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/app/Dialog.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/app/DirectAction.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/app/FragmentManager.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/app/FragmentTransaction.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/app/LoaderManager.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/app/PictureInPictureParams.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/app/PictureInPictureUiState.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/app/RemoteAction.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/app/SharedElementCallback.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/app/TaskInfo.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/app/TaskStackBuilder.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/app/VoiceInteractor.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/app/assist/AssistContent.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/content/DialogInterface.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/content/Loader.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/net/http/SslCertificate.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/net/http/SslError.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/os/Debug.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/print/PageRange.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/print/PrintAttributes.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/print/PrintDocumentAdapter.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/print/PrintDocumentInfo.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/transition/PathMotion.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/transition/Scene.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/transition/Transition.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/transition/TransitionManager.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/transition/TransitionPropagation.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/transition/TransitionValues.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/view/DragAndDropPermissions.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/view/FrameMetrics.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/view/InputQueue.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/view/KeyboardShortcutGroup.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/view/KeyboardShortcutInfo.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/view/SearchEvent.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/view/SurfaceHolder.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/view/Window.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/view/WindowManager.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/view/WindowMetrics.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/ConversationAction.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/ConversationActions.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/SelectionEvent.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextClassification.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextClassificationContext.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextClassificationSessionId.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextClassifier.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextClassifierEvent.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextLanguage.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextSelection.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/webkit/ClientCertRequest.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/webkit/ConsoleMessage.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/webkit/DownloadListener.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/webkit/GeolocationPermissions.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/webkit/HttpAuthHandler.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/webkit/JsPromptResult.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/webkit/JsResult.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/webkit/PermissionRequest.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/webkit/RenderProcessGoneDetail.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/webkit/SafeBrowsingResponse.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/webkit/SslErrorHandler.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/webkit/ValueCallback.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/webkit/WebBackForwardList.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/webkit/WebChromeClient.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/webkit/WebHistoryItem.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/webkit/WebMessage.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/webkit/WebMessagePort.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/webkit/WebResourceError.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/webkit/WebStorage.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/webkit/WebViewRenderProcess.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/webkit/WebViewRenderProcessClient.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/widget/AbsListView.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/widget/AbsoluteLayout.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/widget/Adapter.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/widget/AdapterView.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/widget/Filter.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/widget/FrameLayout.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/widget/ListAdapter.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/widget/ListView.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/widget/SpinnerAdapter.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/widget/Toolbar.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/window/SplashScreen.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/window/SplashScreenView.java diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/ActionBar.java b/java/ql/test/stubs/google-android-9.0.0/android/app/ActionBar.java new file mode 100644 index 00000000000..ae42ac532dc --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/ActionBar.java @@ -0,0 +1,132 @@ +// Generated automatically from android.app.ActionBar for testing purposes + +package android.app; + +import android.app.FragmentTransaction; +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.SpinnerAdapter; + +abstract public class ActionBar +{ + abstract static public class Tab + { + public Tab(){} + public abstract ActionBar.Tab setContentDescription(CharSequence p0); + public abstract ActionBar.Tab setContentDescription(int p0); + public abstract ActionBar.Tab setCustomView(View p0); + public abstract ActionBar.Tab setCustomView(int p0); + public abstract ActionBar.Tab setIcon(Drawable p0); + public abstract ActionBar.Tab setIcon(int p0); + public abstract ActionBar.Tab setTabListener(ActionBar.TabListener p0); + public abstract ActionBar.Tab setTag(Object p0); + public abstract ActionBar.Tab setText(CharSequence p0); + public abstract ActionBar.Tab setText(int p0); + public abstract CharSequence getContentDescription(); + public abstract CharSequence getText(); + public abstract Drawable getIcon(); + public abstract Object getTag(); + public abstract View getCustomView(); + public abstract int getPosition(); + public abstract void select(); + public static int INVALID_POSITION = 0; + } + public ActionBar(){} + public Context getThemedContext(){ return null; } + public abstract ActionBar.Tab getSelectedTab(); + public abstract ActionBar.Tab getTabAt(int p0); + public abstract ActionBar.Tab newTab(); + public abstract CharSequence getSubtitle(); + public abstract CharSequence getTitle(); + public abstract View getCustomView(); + public abstract boolean isShowing(); + public abstract int getDisplayOptions(); + public abstract int getHeight(); + public abstract int getNavigationItemCount(); + public abstract int getNavigationMode(); + public abstract int getSelectedNavigationIndex(); + public abstract int getTabCount(); + public abstract void addOnMenuVisibilityListener(ActionBar.OnMenuVisibilityListener p0); + public abstract void addTab(ActionBar.Tab p0); + public abstract void addTab(ActionBar.Tab p0, boolean p1); + public abstract void addTab(ActionBar.Tab p0, int p1); + public abstract void addTab(ActionBar.Tab p0, int p1, boolean p2); + public abstract void hide(); + public abstract void removeAllTabs(); + public abstract void removeOnMenuVisibilityListener(ActionBar.OnMenuVisibilityListener p0); + public abstract void removeTab(ActionBar.Tab p0); + public abstract void removeTabAt(int p0); + public abstract void selectTab(ActionBar.Tab p0); + public abstract void setBackgroundDrawable(Drawable p0); + public abstract void setCustomView(View p0); + public abstract void setCustomView(View p0, ActionBar.LayoutParams p1); + public abstract void setCustomView(int p0); + public abstract void setDisplayHomeAsUpEnabled(boolean p0); + public abstract void setDisplayOptions(int p0); + public abstract void setDisplayOptions(int p0, int p1); + public abstract void setDisplayShowCustomEnabled(boolean p0); + public abstract void setDisplayShowHomeEnabled(boolean p0); + public abstract void setDisplayShowTitleEnabled(boolean p0); + public abstract void setDisplayUseLogoEnabled(boolean p0); + public abstract void setIcon(Drawable p0); + public abstract void setIcon(int p0); + public abstract void setListNavigationCallbacks(SpinnerAdapter p0, ActionBar.OnNavigationListener p1); + public abstract void setLogo(Drawable p0); + public abstract void setLogo(int p0); + public abstract void setNavigationMode(int p0); + public abstract void setSelectedNavigationItem(int p0); + public abstract void setSubtitle(CharSequence p0); + public abstract void setSubtitle(int p0); + public abstract void setTitle(CharSequence p0); + public abstract void setTitle(int p0); + public abstract void show(); + public boolean isHideOnContentScrollEnabled(){ return false; } + public float getElevation(){ return 0; } + public int getHideOffset(){ return 0; } + public static int DISPLAY_HOME_AS_UP = 0; + public static int DISPLAY_SHOW_CUSTOM = 0; + public static int DISPLAY_SHOW_HOME = 0; + public static int DISPLAY_SHOW_TITLE = 0; + public static int DISPLAY_USE_LOGO = 0; + public static int NAVIGATION_MODE_LIST = 0; + public static int NAVIGATION_MODE_STANDARD = 0; + public static int NAVIGATION_MODE_TABS = 0; + public void setElevation(float p0){} + public void setHideOffset(int p0){} + public void setHideOnContentScrollEnabled(boolean p0){} + public void setHomeActionContentDescription(CharSequence p0){} + public void setHomeActionContentDescription(int p0){} + public void setHomeAsUpIndicator(Drawable p0){} + public void setHomeAsUpIndicator(int p0){} + public void setHomeButtonEnabled(boolean p0){} + public void setSplitBackgroundDrawable(Drawable p0){} + public void setStackedBackgroundDrawable(Drawable p0){} + static public class LayoutParams extends ViewGroup.MarginLayoutParams + { + protected LayoutParams() {} + public LayoutParams(ActionBar.LayoutParams p0){} + public LayoutParams(Context p0, AttributeSet p1){} + public LayoutParams(ViewGroup.LayoutParams p0){} + public LayoutParams(int p0){} + public LayoutParams(int p0, int p1){} + public LayoutParams(int p0, int p1, int p2){} + public int gravity = 0; + } + static public interface OnMenuVisibilityListener + { + void onMenuVisibilityChanged(boolean p0); + } + static public interface OnNavigationListener + { + boolean onNavigationItemSelected(int p0, long p1); + } + static public interface TabListener + { + void onTabReselected(ActionBar.Tab p0, FragmentTransaction p1); + void onTabSelected(ActionBar.Tab p0, FragmentTransaction p1); + void onTabUnselected(ActionBar.Tab p0, FragmentTransaction p1); + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/ActivityManager.java b/java/ql/test/stubs/google-android-9.0.0/android/app/ActivityManager.java new file mode 100644 index 00000000000..2d9bc359cf7 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/ActivityManager.java @@ -0,0 +1,215 @@ +// Generated automatically from android.app.ActivityManager for testing purposes + +package android.app; + +import android.app.Activity; +import android.app.ApplicationExitInfo; +import android.app.PendingIntent; +import android.app.TaskInfo; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ConfigurationInfo; +import android.graphics.Bitmap; +import android.os.Bundle; +import android.os.Debug; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Size; +import java.io.FileDescriptor; +import java.util.List; + +public class ActivityManager +{ + public ConfigurationInfo getDeviceConfigurationInfo(){ return null; } + public Debug.MemoryInfo[] getProcessMemoryInfo(int[] p0){ return null; } + public List getAppTasks(){ return null; } + public List getProcessesInErrorState(){ return null; } + public List getRecentTasks(int p0, int p1){ return null; } + public List getRunningAppProcesses(){ return null; } + public List getRunningServices(int p0){ return null; } + public List getRunningTasks(int p0){ return null; } + public List getHistoricalProcessExitReasons(String p0, int p1, int p2){ return null; } + public PendingIntent getRunningServiceControlPanel(ComponentName p0){ return null; } + public Size getAppTaskThumbnailSize(){ return null; } + public boolean clearApplicationUserData(){ return false; } + public boolean isActivityStartAllowedOnDisplay(Context p0, int p1, Intent p2){ return false; } + public boolean isBackgroundRestricted(){ return false; } + public boolean isInLockTaskMode(){ return false; } + public boolean isLowRamDevice(){ return false; } + public int addAppTask(Activity p0, Intent p1, ActivityManager.TaskDescription p2, Bitmap p3){ return 0; } + public int getLargeMemoryClass(){ return 0; } + public int getLauncherLargeIconDensity(){ return 0; } + public int getLauncherLargeIconSize(){ return 0; } + public int getLockTaskModeState(){ return 0; } + public int getMemoryClass(){ return 0; } + public static String ACTION_REPORT_HEAP_LIMIT = null; + public static String META_HOME_ALTERNATE = null; + public static boolean isLowMemoryKillReportSupported(){ return false; } + public static boolean isRunningInTestHarness(){ return false; } + public static boolean isRunningInUserTestHarness(){ return false; } + public static boolean isUserAMonkey(){ return false; } + public static int LOCK_TASK_MODE_LOCKED = 0; + public static int LOCK_TASK_MODE_NONE = 0; + public static int LOCK_TASK_MODE_PINNED = 0; + public static int MOVE_TASK_NO_USER_ACTION = 0; + public static int MOVE_TASK_WITH_HOME = 0; + public static int RECENT_IGNORE_UNAVAILABLE = 0; + public static int RECENT_WITH_EXCLUDED = 0; + public static void getMyMemoryState(ActivityManager.RunningAppProcessInfo p0){} + public static void setVrThread(int p0){} + public void appNotResponding(String p0){} + public void clearWatchHeapLimit(){} + public void dumpPackageState(FileDescriptor p0, String p1){} + public void getMemoryInfo(ActivityManager.MemoryInfo p0){} + public void killBackgroundProcesses(String p0){} + public void moveTaskToFront(int p0, int p1){} + public void moveTaskToFront(int p0, int p1, Bundle p2){} + public void restartPackage(String p0){} + public void setProcessStateSummary(byte[] p0){} + public void setWatchHeapLimit(long p0){} + static public class AppTask + { + public ActivityManager.RecentTaskInfo getTaskInfo(){ return null; } + public void finishAndRemoveTask(){} + public void moveToFront(){} + public void setExcludeFromRecents(boolean p0){} + public void startActivity(Context p0, Intent p1, Bundle p2){} + } + static public class MemoryInfo implements Parcelable + { + public MemoryInfo(){} + public boolean lowMemory = false; + public int describeContents(){ return 0; } + public long availMem = 0; + public long threshold = 0; + public long totalMem = 0; + public static Parcelable.Creator CREATOR = null; + public void readFromParcel(Parcel p0){} + public void writeToParcel(Parcel p0, int p1){} + } + static public class ProcessErrorStateInfo implements Parcelable + { + public ProcessErrorStateInfo(){} + public String longMsg = null; + public String processName = null; + public String shortMsg = null; + public String stackTrace = null; + public String tag = null; + public byte[] crashData = null; + public int condition = 0; + public int describeContents(){ return 0; } + public int pid = 0; + public int uid = 0; + public static Parcelable.Creator CREATOR = null; + public static int CRASHED = 0; + public static int NOT_RESPONDING = 0; + public static int NO_ERROR = 0; + public void readFromParcel(Parcel p0){} + public void writeToParcel(Parcel p0, int p1){} + } + static public class RecentTaskInfo extends TaskInfo implements Parcelable + { + public CharSequence description = null; + public RecentTaskInfo(){} + public int affiliatedTaskId = 0; + public int describeContents(){ return 0; } + public int id = 0; + public int persistentId = 0; + public static Parcelable.Creator CREATOR = null; + public void readFromParcel(Parcel p0){} + public void writeToParcel(Parcel p0, int p1){} + } + static public class RunningAppProcessInfo implements Parcelable + { + public ComponentName importanceReasonComponent = null; + public RunningAppProcessInfo(){} + public RunningAppProcessInfo(String p0, int p1, String[] p2){} + public String processName = null; + public String[] pkgList = null; + public int describeContents(){ return 0; } + public int importance = 0; + public int importanceReasonCode = 0; + public int importanceReasonPid = 0; + public int lastTrimLevel = 0; + public int lru = 0; + public int pid = 0; + public int uid = 0; + public static Parcelable.Creator CREATOR = null; + public static int IMPORTANCE_BACKGROUND = 0; + public static int IMPORTANCE_CACHED = 0; + public static int IMPORTANCE_CANT_SAVE_STATE = 0; + public static int IMPORTANCE_EMPTY = 0; + public static int IMPORTANCE_FOREGROUND = 0; + public static int IMPORTANCE_FOREGROUND_SERVICE = 0; + public static int IMPORTANCE_GONE = 0; + public static int IMPORTANCE_PERCEPTIBLE = 0; + public static int IMPORTANCE_PERCEPTIBLE_PRE_26 = 0; + public static int IMPORTANCE_SERVICE = 0; + public static int IMPORTANCE_TOP_SLEEPING = 0; + public static int IMPORTANCE_TOP_SLEEPING_PRE_28 = 0; + public static int IMPORTANCE_VISIBLE = 0; + public static int REASON_PROVIDER_IN_USE = 0; + public static int REASON_SERVICE_IN_USE = 0; + public static int REASON_UNKNOWN = 0; + public void readFromParcel(Parcel p0){} + public void writeToParcel(Parcel p0, int p1){} + } + static public class RunningServiceInfo implements Parcelable + { + public ComponentName service = null; + public RunningServiceInfo(){} + public String clientPackage = null; + public String process = null; + public boolean foreground = false; + public boolean started = false; + public int clientCount = 0; + public int clientLabel = 0; + public int crashCount = 0; + public int describeContents(){ return 0; } + public int flags = 0; + public int pid = 0; + public int uid = 0; + public long activeSince = 0; + public long lastActivityTime = 0; + public long restarting = 0; + public static Parcelable.Creator CREATOR = null; + public static int FLAG_FOREGROUND = 0; + public static int FLAG_PERSISTENT_PROCESS = 0; + public static int FLAG_STARTED = 0; + public static int FLAG_SYSTEM_PROCESS = 0; + public void readFromParcel(Parcel p0){} + public void writeToParcel(Parcel p0, int p1){} + } + static public class RunningTaskInfo extends TaskInfo implements Parcelable + { + public Bitmap thumbnail = null; + public CharSequence description = null; + public RunningTaskInfo(){} + public int describeContents(){ return 0; } + public int id = 0; + public int numRunning = 0; + public static Parcelable.Creator CREATOR = null; + public void readFromParcel(Parcel p0){} + public void writeToParcel(Parcel p0, int p1){} + } + static public class TaskDescription implements Parcelable + { + public Bitmap getIcon(){ return null; } + public String getLabel(){ return null; } + public String toString(){ return null; } + public TaskDescription(){} + public TaskDescription(ActivityManager.TaskDescription p0){} + public TaskDescription(String p0){} + public TaskDescription(String p0, Bitmap p1){} + public TaskDescription(String p0, Bitmap p1, int p2){} + public TaskDescription(String p0, int p1){} + public TaskDescription(String p0, int p1, int p2){} + public boolean equals(Object p0){ return false; } + public int describeContents(){ return 0; } + public int getPrimaryColor(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void readFromParcel(Parcel p0){} + public void writeToParcel(Parcel p0, int p1){} + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/AlertDialog.java b/java/ql/test/stubs/google-android-9.0.0/android/app/AlertDialog.java new file mode 100644 index 00000000000..530cccf7bc1 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/AlertDialog.java @@ -0,0 +1,95 @@ +// Generated automatically from android.app.AlertDialog for testing purposes + +package android.app; + +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.database.Cursor; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.os.Message; +import android.view.KeyEvent; +import android.view.View; +import android.widget.Adapter; +import android.widget.AdapterView; +import android.widget.Button; +import android.widget.ListAdapter; +import android.widget.ListView; + +public class AlertDialog extends Dialog implements DialogInterface +{ + protected AlertDialog() {} + protected AlertDialog(Context p0){} + protected AlertDialog(Context p0, boolean p1, DialogInterface.OnCancelListener p2){} + protected AlertDialog(Context p0, int p1){} + protected void onCreate(Bundle p0){} + public Button getButton(int p0){ return null; } + public ListView getListView(){ return null; } + public boolean onKeyDown(int p0, KeyEvent p1){ return false; } + public boolean onKeyUp(int p0, KeyEvent p1){ return false; } + public static int THEME_DEVICE_DEFAULT_DARK = 0; + public static int THEME_DEVICE_DEFAULT_LIGHT = 0; + public static int THEME_HOLO_DARK = 0; + public static int THEME_HOLO_LIGHT = 0; + public static int THEME_TRADITIONAL = 0; + public void setButton(CharSequence p0, DialogInterface.OnClickListener p1){} + public void setButton(CharSequence p0, Message p1){} + public void setButton(int p0, CharSequence p1, DialogInterface.OnClickListener p2){} + public void setButton(int p0, CharSequence p1, Message p2){} + public void setButton2(CharSequence p0, DialogInterface.OnClickListener p1){} + public void setButton2(CharSequence p0, Message p1){} + public void setButton3(CharSequence p0, DialogInterface.OnClickListener p1){} + public void setButton3(CharSequence p0, Message p1){} + public void setCustomTitle(View p0){} + public void setIcon(Drawable p0){} + public void setIcon(int p0){} + public void setIconAttribute(int p0){} + public void setInverseBackgroundForced(boolean p0){} + public void setMessage(CharSequence p0){} + public void setTitle(CharSequence p0){} + public void setView(View p0){} + public void setView(View p0, int p1, int p2, int p3, int p4){} + static public class Builder + { + protected Builder() {} + public AlertDialog create(){ return null; } + public AlertDialog show(){ return null; } + public AlertDialog.Builder setAdapter(ListAdapter p0, DialogInterface.OnClickListener p1){ return null; } + public AlertDialog.Builder setCancelable(boolean p0){ return null; } + public AlertDialog.Builder setCursor(Cursor p0, DialogInterface.OnClickListener p1, String p2){ return null; } + public AlertDialog.Builder setCustomTitle(View p0){ return null; } + public AlertDialog.Builder setIcon(Drawable p0){ return null; } + public AlertDialog.Builder setIcon(int p0){ return null; } + public AlertDialog.Builder setIconAttribute(int p0){ return null; } + public AlertDialog.Builder setInverseBackgroundForced(boolean p0){ return null; } + public AlertDialog.Builder setItems(CharSequence[] p0, DialogInterface.OnClickListener p1){ return null; } + public AlertDialog.Builder setItems(int p0, DialogInterface.OnClickListener p1){ return null; } + public AlertDialog.Builder setMessage(CharSequence p0){ return null; } + public AlertDialog.Builder setMessage(int p0){ return null; } + public AlertDialog.Builder setMultiChoiceItems(CharSequence[] p0, boolean[] p1, DialogInterface.OnMultiChoiceClickListener p2){ return null; } + public AlertDialog.Builder setMultiChoiceItems(Cursor p0, String p1, String p2, DialogInterface.OnMultiChoiceClickListener p3){ return null; } + public AlertDialog.Builder setMultiChoiceItems(int p0, boolean[] p1, DialogInterface.OnMultiChoiceClickListener p2){ return null; } + public AlertDialog.Builder setNegativeButton(CharSequence p0, DialogInterface.OnClickListener p1){ return null; } + public AlertDialog.Builder setNegativeButton(int p0, DialogInterface.OnClickListener p1){ return null; } + public AlertDialog.Builder setNeutralButton(CharSequence p0, DialogInterface.OnClickListener p1){ return null; } + public AlertDialog.Builder setNeutralButton(int p0, DialogInterface.OnClickListener p1){ return null; } + public AlertDialog.Builder setOnCancelListener(DialogInterface.OnCancelListener p0){ return null; } + public AlertDialog.Builder setOnDismissListener(DialogInterface.OnDismissListener p0){ return null; } + public AlertDialog.Builder setOnItemSelectedListener(AdapterView.OnItemSelectedListener p0){ return null; } + public AlertDialog.Builder setOnKeyListener(DialogInterface.OnKeyListener p0){ return null; } + public AlertDialog.Builder setPositiveButton(CharSequence p0, DialogInterface.OnClickListener p1){ return null; } + public AlertDialog.Builder setPositiveButton(int p0, DialogInterface.OnClickListener p1){ return null; } + public AlertDialog.Builder setSingleChoiceItems(CharSequence[] p0, int p1, DialogInterface.OnClickListener p2){ return null; } + public AlertDialog.Builder setSingleChoiceItems(Cursor p0, int p1, String p2, DialogInterface.OnClickListener p3){ return null; } + public AlertDialog.Builder setSingleChoiceItems(ListAdapter p0, int p1, DialogInterface.OnClickListener p2){ return null; } + public AlertDialog.Builder setSingleChoiceItems(int p0, int p1, DialogInterface.OnClickListener p2){ return null; } + public AlertDialog.Builder setTitle(CharSequence p0){ return null; } + public AlertDialog.Builder setTitle(int p0){ return null; } + public AlertDialog.Builder setView(View p0){ return null; } + public AlertDialog.Builder setView(int p0){ return null; } + public Builder(Context p0){} + public Builder(Context p0, int p1){} + public Context getContext(){ return null; } + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/Application.java b/java/ql/test/stubs/google-android-9.0.0/android/app/Application.java new file mode 100644 index 00000000000..e9199a2da83 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/Application.java @@ -0,0 +1,55 @@ +// Generated automatically from android.app.Application for testing purposes + +package android.app; + +import android.app.Activity; +import android.content.ComponentCallbacks2; +import android.content.ComponentCallbacks; +import android.content.ContextWrapper; +import android.content.res.Configuration; +import android.os.Bundle; + +public class Application extends ContextWrapper implements ComponentCallbacks2 +{ + public Application(){} + public static String getProcessName(){ return null; } + public void onConfigurationChanged(Configuration p0){} + public void onCreate(){} + public void onLowMemory(){} + public void onTerminate(){} + public void onTrimMemory(int p0){} + public void registerActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacks p0){} + public void registerComponentCallbacks(ComponentCallbacks p0){} + public void registerOnProvideAssistDataListener(Application.OnProvideAssistDataListener p0){} + public void unregisterActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacks p0){} + public void unregisterComponentCallbacks(ComponentCallbacks p0){} + public void unregisterOnProvideAssistDataListener(Application.OnProvideAssistDataListener p0){} + static public interface ActivityLifecycleCallbacks + { + default void onActivityPostCreated(Activity p0, Bundle p1){} + default void onActivityPostDestroyed(Activity p0){} + default void onActivityPostPaused(Activity p0){} + default void onActivityPostResumed(Activity p0){} + default void onActivityPostSaveInstanceState(Activity p0, Bundle p1){} + default void onActivityPostStarted(Activity p0){} + default void onActivityPostStopped(Activity p0){} + default void onActivityPreCreated(Activity p0, Bundle p1){} + default void onActivityPreDestroyed(Activity p0){} + default void onActivityPrePaused(Activity p0){} + default void onActivityPreResumed(Activity p0){} + default void onActivityPreSaveInstanceState(Activity p0, Bundle p1){} + default void onActivityPreStarted(Activity p0){} + default void onActivityPreStopped(Activity p0){} + void onActivityCreated(Activity p0, Bundle p1); + void onActivityDestroyed(Activity p0); + void onActivityPaused(Activity p0); + void onActivityResumed(Activity p0); + void onActivitySaveInstanceState(Activity p0, Bundle p1); + void onActivityStarted(Activity p0); + void onActivityStopped(Activity p0); + } + static public interface OnProvideAssistDataListener + { + void onProvideAssistData(Activity p0, Bundle p1); + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/ApplicationExitInfo.java b/java/ql/test/stubs/google-android-9.0.0/android/app/ApplicationExitInfo.java new file mode 100644 index 00000000000..6f412aa122a --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/ApplicationExitInfo.java @@ -0,0 +1,47 @@ +// Generated automatically from android.app.ApplicationExitInfo for testing purposes + +package android.app; + +import android.os.Parcel; +import android.os.Parcelable; +import android.os.UserHandle; +import java.io.InputStream; + +public class ApplicationExitInfo implements Parcelable +{ + public InputStream getTraceInputStream(){ return null; } + public String getDescription(){ return null; } + public String getProcessName(){ return null; } + public String toString(){ return null; } + public UserHandle getUserHandle(){ return null; } + public boolean equals(Object p0){ return false; } + public byte[] getProcessStateSummary(){ return null; } + public int describeContents(){ return 0; } + public int getDefiningUid(){ return 0; } + public int getImportance(){ return 0; } + public int getPackageUid(){ return 0; } + public int getPid(){ return 0; } + public int getRealUid(){ return 0; } + public int getReason(){ return 0; } + public int getStatus(){ return 0; } + public int hashCode(){ return 0; } + public long getPss(){ return 0; } + public long getRss(){ return 0; } + public long getTimestamp(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static int REASON_ANR = 0; + public static int REASON_CRASH = 0; + public static int REASON_CRASH_NATIVE = 0; + public static int REASON_DEPENDENCY_DIED = 0; + public static int REASON_EXCESSIVE_RESOURCE_USAGE = 0; + public static int REASON_EXIT_SELF = 0; + public static int REASON_INITIALIZATION_FAILURE = 0; + public static int REASON_LOW_MEMORY = 0; + public static int REASON_OTHER = 0; + public static int REASON_PERMISSION_CHANGE = 0; + public static int REASON_SIGNALED = 0; + public static int REASON_UNKNOWN = 0; + public static int REASON_USER_REQUESTED = 0; + public static int REASON_USER_STOPPED = 0; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/Dialog.java b/java/ql/test/stubs/google-android-9.0.0/android/app/Dialog.java new file mode 100644 index 00000000000..35c4b8bb393 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/Dialog.java @@ -0,0 +1,121 @@ +// Generated automatically from android.app.Dialog for testing purposes + +package android.app; + +import android.app.ActionBar; +import android.app.Activity; +import android.content.Context; +import android.content.DialogInterface; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Bundle; +import android.os.Message; +import android.view.ActionMode; +import android.view.ContextMenu; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.SearchEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; +import android.view.accessibility.AccessibilityEvent; + +public class Dialog implements DialogInterface, KeyEvent.Callback, View.OnCreateContextMenuListener, Window.Callback +{ + protected Dialog() {} + protected Dialog(Context p0, boolean p1, DialogInterface.OnCancelListener p2){} + protected void onCreate(Bundle p0){} + protected void onStart(){} + protected void onStop(){} + public T findViewById(int p0){ return null; } + public ActionBar getActionBar(){ return null; } + public ActionMode onWindowStartingActionMode(ActionMode.Callback p0){ return null; } + public ActionMode onWindowStartingActionMode(ActionMode.Callback p0, int p1){ return null; } + public Bundle onSaveInstanceState(){ return null; } + public Dialog(Context p0){} + public Dialog(Context p0, int p1){} + public LayoutInflater getLayoutInflater(){ return null; } + public View getCurrentFocus(){ return null; } + public View onCreatePanelView(int p0){ return null; } + public Window getWindow(){ return null; } + public boolean dispatchGenericMotionEvent(MotionEvent p0){ return false; } + public boolean dispatchKeyEvent(KeyEvent p0){ return false; } + public boolean dispatchKeyShortcutEvent(KeyEvent p0){ return false; } + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent p0){ return false; } + public boolean dispatchTouchEvent(MotionEvent p0){ return false; } + public boolean dispatchTrackballEvent(MotionEvent p0){ return false; } + public boolean isShowing(){ return false; } + public boolean onContextItemSelected(MenuItem p0){ return false; } + public boolean onCreateOptionsMenu(Menu p0){ return false; } + public boolean onCreatePanelMenu(int p0, Menu p1){ return false; } + public boolean onGenericMotionEvent(MotionEvent p0){ return false; } + public boolean onKeyDown(int p0, KeyEvent p1){ return false; } + public boolean onKeyLongPress(int p0, KeyEvent p1){ return false; } + public boolean onKeyMultiple(int p0, int p1, KeyEvent p2){ return false; } + public boolean onKeyShortcut(int p0, KeyEvent p1){ return false; } + public boolean onKeyUp(int p0, KeyEvent p1){ return false; } + public boolean onMenuItemSelected(int p0, MenuItem p1){ return false; } + public boolean onMenuOpened(int p0, Menu p1){ return false; } + public boolean onOptionsItemSelected(MenuItem p0){ return false; } + public boolean onPrepareOptionsMenu(Menu p0){ return false; } + public boolean onPreparePanel(int p0, View p1, Menu p2){ return false; } + public boolean onSearchRequested(){ return false; } + public boolean onSearchRequested(SearchEvent p0){ return false; } + public boolean onTouchEvent(MotionEvent p0){ return false; } + public boolean onTrackballEvent(MotionEvent p0){ return false; } + public final T requireViewById(int p0){ return null; } + public final Activity getOwnerActivity(){ return null; } + public final Context getContext(){ return null; } + public final SearchEvent getSearchEvent(){ return null; } + public final boolean requestWindowFeature(int p0){ return false; } + public final int getVolumeControlStream(){ return 0; } + public final void setFeatureDrawable(int p0, Drawable p1){} + public final void setFeatureDrawableAlpha(int p0, int p1){} + public final void setFeatureDrawableResource(int p0, int p1){} + public final void setFeatureDrawableUri(int p0, Uri p1){} + public final void setOwnerActivity(Activity p0){} + public final void setVolumeControlStream(int p0){} + public void addContentView(View p0, ViewGroup.LayoutParams p1){} + public void cancel(){} + public void closeOptionsMenu(){} + public void create(){} + public void dismiss(){} + public void hide(){} + public void invalidateOptionsMenu(){} + public void onActionModeFinished(ActionMode p0){} + public void onActionModeStarted(ActionMode p0){} + public void onAttachedToWindow(){} + public void onBackPressed(){} + public void onContentChanged(){} + public void onContextMenuClosed(Menu p0){} + public void onCreateContextMenu(ContextMenu p0, View p1, ContextMenu.ContextMenuInfo p2){} + public void onDetachedFromWindow(){} + public void onOptionsMenuClosed(Menu p0){} + public void onPanelClosed(int p0, Menu p1){} + public void onRestoreInstanceState(Bundle p0){} + public void onWindowAttributesChanged(WindowManager.LayoutParams p0){} + public void onWindowFocusChanged(boolean p0){} + public void openContextMenu(View p0){} + public void openOptionsMenu(){} + public void registerForContextMenu(View p0){} + public void setCancelMessage(Message p0){} + public void setCancelable(boolean p0){} + public void setCanceledOnTouchOutside(boolean p0){} + public void setContentView(View p0){} + public void setContentView(View p0, ViewGroup.LayoutParams p1){} + public void setContentView(int p0){} + public void setDismissMessage(Message p0){} + public void setOnCancelListener(DialogInterface.OnCancelListener p0){} + public void setOnDismissListener(DialogInterface.OnDismissListener p0){} + public void setOnKeyListener(DialogInterface.OnKeyListener p0){} + public void setOnShowListener(DialogInterface.OnShowListener p0){} + public void setTitle(CharSequence p0){} + public void setTitle(int p0){} + public void show(){} + public void takeKeyEvents(boolean p0){} + public void unregisterForContextMenu(View p0){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/DirectAction.java b/java/ql/test/stubs/google-android-9.0.0/android/app/DirectAction.java new file mode 100644 index 00000000000..7eeaa9eac66 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/DirectAction.java @@ -0,0 +1,20 @@ +// Generated automatically from android.app.DirectAction for testing purposes + +package android.app; + +import android.content.LocusId; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +public class DirectAction implements Parcelable +{ + public Bundle getExtras(){ return null; } + public LocusId getLocusId(){ return null; } + public String getId(){ return null; } + public boolean equals(Object p0){ return false; } + public int describeContents(){ return 0; } + public int hashCode(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/Fragment.java b/java/ql/test/stubs/google-android-9.0.0/android/app/Fragment.java index 5e5f71dcd25..41969f5d877 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/app/Fragment.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/Fragment.java @@ -1,82 +1,148 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ +// Generated automatically from android.app.Fragment for testing purposes package android.app; -import android.annotation.Nullable; +import android.animation.Animator; +import android.app.Activity; +import android.app.FragmentManager; +import android.app.LoaderManager; +import android.app.SharedElementCallback; import android.content.ComponentCallbacks2; import android.content.Context; import android.content.Intent; +import android.content.IntentSender; import android.content.res.Configuration; +import android.content.res.Resources; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import android.transition.Transition; +import android.util.AttributeSet; +import android.view.ContextMenu; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import java.io.FileDescriptor; +import java.io.PrintWriter; -public class Fragment implements ComponentCallbacks2 { - public static class SavedState implements Parcelable { - @Override - public int describeContents() { - return 0; +public class Fragment implements ComponentCallbacks2, View.OnCreateContextMenuListener +{ + public Animator onCreateAnimator(int p0, boolean p1, int p2){ return null; } + public Context getContext(){ return null; } + public Fragment(){} + public LayoutInflater onGetLayoutInflater(Bundle p0){ return null; } + public LoaderManager getLoaderManager(){ return null; } + public String toString(){ return null; } + public Transition getEnterTransition(){ return null; } + public Transition getExitTransition(){ return null; } + public Transition getReenterTransition(){ return null; } + public Transition getReturnTransition(){ return null; } + public Transition getSharedElementEnterTransition(){ return null; } + public Transition getSharedElementReturnTransition(){ return null; } + public View getView(){ return null; } + public View onCreateView(LayoutInflater p0, ViewGroup p1, Bundle p2){ return null; } + public boolean getAllowEnterTransitionOverlap(){ return false; } + public boolean getAllowReturnTransitionOverlap(){ return false; } + public boolean getUserVisibleHint(){ return false; } + public boolean onContextItemSelected(MenuItem p0){ return false; } + public boolean onOptionsItemSelected(MenuItem p0){ return false; } + public boolean shouldShowRequestPermissionRationale(String p0){ return false; } + public final Activity getActivity(){ return null; } + public final Bundle getArguments(){ return null; } + public final CharSequence getText(int p0){ return null; } + public final Fragment getParentFragment(){ return null; } + public final Fragment getTargetFragment(){ return null; } + public final FragmentManager getChildFragmentManager(){ return null; } + public final FragmentManager getFragmentManager(){ return null; } + public final LayoutInflater getLayoutInflater(){ return null; } + public final Object getHost(){ return null; } + public final Resources getResources(){ return null; } + public final String getString(int p0){ return null; } + public final String getString(int p0, Object... p1){ return null; } + public final String getTag(){ return null; } + public final boolean equals(Object p0){ return false; } + public final boolean getRetainInstance(){ return false; } + public final boolean isAdded(){ return false; } + public final boolean isDetached(){ return false; } + public final boolean isHidden(){ return false; } + public final boolean isInLayout(){ return false; } + public final boolean isRemoving(){ return false; } + public final boolean isResumed(){ return false; } + public final boolean isStateSaved(){ return false; } + public final boolean isVisible(){ return false; } + public final int getId(){ return 0; } + public final int getTargetRequestCode(){ return 0; } + public final int hashCode(){ return 0; } + public final void requestPermissions(String[] p0, int p1){} + public static Fragment instantiate(Context p0, String p1){ return null; } + public static Fragment instantiate(Context p0, String p1, Bundle p2){ return null; } + public void dump(String p0, FileDescriptor p1, PrintWriter p2, String[] p3){} + public void onActivityCreated(Bundle p0){} + public void onActivityResult(int p0, int p1, Intent p2){} + public void onAttach(Activity p0){} + public void onAttach(Context p0){} + public void onAttachFragment(Fragment p0){} + public void onConfigurationChanged(Configuration p0){} + public void onCreate(Bundle p0){} + public void onCreateContextMenu(ContextMenu p0, View p1, ContextMenu.ContextMenuInfo p2){} + public void onCreateOptionsMenu(Menu p0, MenuInflater p1){} + public void onDestroy(){} + public void onDestroyOptionsMenu(){} + public void onDestroyView(){} + public void onDetach(){} + public void onHiddenChanged(boolean p0){} + public void onInflate(Activity p0, AttributeSet p1, Bundle p2){} + public void onInflate(AttributeSet p0, Bundle p1){} + public void onInflate(Context p0, AttributeSet p1, Bundle p2){} + public void onLowMemory(){} + public void onMultiWindowModeChanged(boolean p0){} + public void onMultiWindowModeChanged(boolean p0, Configuration p1){} + public void onOptionsMenuClosed(Menu p0){} + public void onPause(){} + public void onPictureInPictureModeChanged(boolean p0){} + public void onPictureInPictureModeChanged(boolean p0, Configuration p1){} + public void onPrepareOptionsMenu(Menu p0){} + public void onRequestPermissionsResult(int p0, String[] p1, int[] p2){} + public void onResume(){} + public void onSaveInstanceState(Bundle p0){} + public void onStart(){} + public void onStop(){} + public void onTrimMemory(int p0){} + public void onViewCreated(View p0, Bundle p1){} + public void onViewStateRestored(Bundle p0){} + public void postponeEnterTransition(){} + public void registerForContextMenu(View p0){} + public void setAllowEnterTransitionOverlap(boolean p0){} + public void setAllowReturnTransitionOverlap(boolean p0){} + public void setArguments(Bundle p0){} + public void setEnterSharedElementCallback(SharedElementCallback p0){} + public void setEnterTransition(Transition p0){} + public void setExitSharedElementCallback(SharedElementCallback p0){} + public void setExitTransition(Transition p0){} + public void setHasOptionsMenu(boolean p0){} + public void setInitialSavedState(Fragment.SavedState p0){} + public void setMenuVisibility(boolean p0){} + public void setReenterTransition(Transition p0){} + public void setRetainInstance(boolean p0){} + public void setReturnTransition(Transition p0){} + public void setSharedElementEnterTransition(Transition p0){} + public void setSharedElementReturnTransition(Transition p0){} + public void setTargetFragment(Fragment p0, int p1){} + public void setUserVisibleHint(boolean p0){} + public void startActivity(Intent p0){} + public void startActivity(Intent p0, Bundle p1){} + public void startActivityForResult(Intent p0, int p1){} + public void startActivityForResult(Intent p0, int p1, Bundle p2){} + public void startIntentSenderForResult(IntentSender p0, int p1, Intent p2, int p3, int p4, int p5, Bundle p6){} + public void startPostponedEnterTransition(){} + public void unregisterForContextMenu(View p0){} + static public class SavedState implements Parcelable + { + public int describeContents(){ return 0; } + public static Parcelable.ClassLoaderCreator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} } - - @Override - public void writeToParcel(Parcel dest, int flags) {} - - } - - static public class InstantiationException { - public InstantiationException(String msg, Exception cause) {} - } - - - public Fragment() {} - - public static Fragment instantiate(Context context, String fname) { - return null; - } - - public static Fragment instantiate(Context context, String fname, @Nullable Bundle args) { - return null; - } - - @Override - final public boolean equals(Object o) { - return false; - } - - @Override - final public int hashCode() { - return 0; - } - - @Override - public String toString() { - return null; - } - - @Override - public void onConfigurationChanged(Configuration p0) {} - - @Override - public void onLowMemory() {} - - @Override - public void onTrimMemory(int p0) {} - - public void startActivityForResult(Intent intent, int requestCode) {} - - public void startActivityForResult(Intent intent, int requestCode, Bundle options) {} - - public void onActivityResult(int requestCode, int resultCode, Intent data) {} } diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/FragmentManager.java b/java/ql/test/stubs/google-android-9.0.0/android/app/FragmentManager.java new file mode 100644 index 00000000000..0d62560b685 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/FragmentManager.java @@ -0,0 +1,75 @@ +// Generated automatically from android.app.FragmentManager for testing purposes + +package android.app; + +import android.app.Fragment; +import android.app.FragmentTransaction; +import android.content.Context; +import android.os.Bundle; +import android.view.View; +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.List; + +abstract public class FragmentManager +{ + abstract static public class FragmentLifecycleCallbacks + { + public FragmentLifecycleCallbacks(){} + public void onFragmentActivityCreated(FragmentManager p0, Fragment p1, Bundle p2){} + public void onFragmentAttached(FragmentManager p0, Fragment p1, Context p2){} + public void onFragmentCreated(FragmentManager p0, Fragment p1, Bundle p2){} + public void onFragmentDestroyed(FragmentManager p0, Fragment p1){} + public void onFragmentDetached(FragmentManager p0, Fragment p1){} + public void onFragmentPaused(FragmentManager p0, Fragment p1){} + public void onFragmentPreAttached(FragmentManager p0, Fragment p1, Context p2){} + public void onFragmentPreCreated(FragmentManager p0, Fragment p1, Bundle p2){} + public void onFragmentResumed(FragmentManager p0, Fragment p1){} + public void onFragmentSaveInstanceState(FragmentManager p0, Fragment p1, Bundle p2){} + public void onFragmentStarted(FragmentManager p0, Fragment p1){} + public void onFragmentStopped(FragmentManager p0, Fragment p1){} + public void onFragmentViewCreated(FragmentManager p0, Fragment p1, View p2, Bundle p3){} + public void onFragmentViewDestroyed(FragmentManager p0, Fragment p1){} + } + public FragmentManager(){} + public abstract Fragment findFragmentById(int p0); + public abstract Fragment findFragmentByTag(String p0); + public abstract Fragment getFragment(Bundle p0, String p1); + public abstract Fragment getPrimaryNavigationFragment(); + public abstract Fragment.SavedState saveFragmentInstanceState(Fragment p0); + public abstract FragmentManager.BackStackEntry getBackStackEntryAt(int p0); + public abstract FragmentTransaction beginTransaction(); + public abstract List getFragments(); + public abstract boolean executePendingTransactions(); + public abstract boolean isDestroyed(); + public abstract boolean isStateSaved(); + public abstract boolean popBackStackImmediate(); + public abstract boolean popBackStackImmediate(String p0, int p1); + public abstract boolean popBackStackImmediate(int p0, int p1); + public abstract int getBackStackEntryCount(); + public abstract void addOnBackStackChangedListener(FragmentManager.OnBackStackChangedListener p0); + public abstract void dump(String p0, FileDescriptor p1, PrintWriter p2, String[] p3); + public abstract void popBackStack(); + public abstract void popBackStack(String p0, int p1); + public abstract void popBackStack(int p0, int p1); + public abstract void putFragment(Bundle p0, String p1, Fragment p2); + public abstract void registerFragmentLifecycleCallbacks(FragmentManager.FragmentLifecycleCallbacks p0, boolean p1); + public abstract void removeOnBackStackChangedListener(FragmentManager.OnBackStackChangedListener p0); + public abstract void unregisterFragmentLifecycleCallbacks(FragmentManager.FragmentLifecycleCallbacks p0); + public static int POP_BACK_STACK_INCLUSIVE = 0; + public static void enableDebugLogging(boolean p0){} + public void invalidateOptionsMenu(){} + static public interface BackStackEntry + { + CharSequence getBreadCrumbShortTitle(); + CharSequence getBreadCrumbTitle(); + String getName(); + int getBreadCrumbShortTitleRes(); + int getBreadCrumbTitleRes(); + int getId(); + } + static public interface OnBackStackChangedListener + { + void onBackStackChanged(); + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/FragmentTransaction.java b/java/ql/test/stubs/google-android-9.0.0/android/app/FragmentTransaction.java new file mode 100644 index 00000000000..762dc14091c --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/FragmentTransaction.java @@ -0,0 +1,48 @@ +// Generated automatically from android.app.FragmentTransaction for testing purposes + +package android.app; + +import android.app.Fragment; +import android.view.View; + +abstract public class FragmentTransaction +{ + public FragmentTransaction(){} + public abstract FragmentTransaction add(Fragment p0, String p1); + public abstract FragmentTransaction add(int p0, Fragment p1); + public abstract FragmentTransaction add(int p0, Fragment p1, String p2); + public abstract FragmentTransaction addSharedElement(View p0, String p1); + public abstract FragmentTransaction addToBackStack(String p0); + public abstract FragmentTransaction attach(Fragment p0); + public abstract FragmentTransaction detach(Fragment p0); + public abstract FragmentTransaction disallowAddToBackStack(); + public abstract FragmentTransaction hide(Fragment p0); + public abstract FragmentTransaction remove(Fragment p0); + public abstract FragmentTransaction replace(int p0, Fragment p1); + public abstract FragmentTransaction replace(int p0, Fragment p1, String p2); + public abstract FragmentTransaction runOnCommit(Runnable p0); + public abstract FragmentTransaction setBreadCrumbShortTitle(CharSequence p0); + public abstract FragmentTransaction setBreadCrumbShortTitle(int p0); + public abstract FragmentTransaction setBreadCrumbTitle(CharSequence p0); + public abstract FragmentTransaction setBreadCrumbTitle(int p0); + public abstract FragmentTransaction setCustomAnimations(int p0, int p1); + public abstract FragmentTransaction setCustomAnimations(int p0, int p1, int p2, int p3); + public abstract FragmentTransaction setPrimaryNavigationFragment(Fragment p0); + public abstract FragmentTransaction setReorderingAllowed(boolean p0); + public abstract FragmentTransaction setTransition(int p0); + public abstract FragmentTransaction setTransitionStyle(int p0); + public abstract FragmentTransaction show(Fragment p0); + public abstract boolean isAddToBackStackAllowed(); + public abstract boolean isEmpty(); + public abstract int commit(); + public abstract int commitAllowingStateLoss(); + public abstract void commitNow(); + public abstract void commitNowAllowingStateLoss(); + public static int TRANSIT_ENTER_MASK = 0; + public static int TRANSIT_EXIT_MASK = 0; + public static int TRANSIT_FRAGMENT_CLOSE = 0; + public static int TRANSIT_FRAGMENT_FADE = 0; + public static int TRANSIT_FRAGMENT_OPEN = 0; + public static int TRANSIT_NONE = 0; + public static int TRANSIT_UNSET = 0; +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/LoaderManager.java b/java/ql/test/stubs/google-android-9.0.0/android/app/LoaderManager.java new file mode 100644 index 00000000000..1c94d938590 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/LoaderManager.java @@ -0,0 +1,25 @@ +// Generated automatically from android.app.LoaderManager for testing purposes + +package android.app; + +import android.content.Loader; +import android.os.Bundle; +import java.io.FileDescriptor; +import java.io.PrintWriter; + +abstract public class LoaderManager +{ + public LoaderManager(){} + public abstract Loader getLoader(int p0); + public abstract Loader initLoader(int p0, Bundle p1, LoaderManager.LoaderCallbacks p2); + public abstract Loader restartLoader(int p0, Bundle p1, LoaderManager.LoaderCallbacks p2); + public abstract void destroyLoader(int p0); + public abstract void dump(String p0, FileDescriptor p1, PrintWriter p2, String[] p3); + public static void enableDebugLogging(boolean p0){} + static public interface LoaderCallbacks + { + Loader onCreateLoader(int p0, Bundle p1); + void onLoadFinished(Loader p0, D p1); + void onLoaderReset(Loader p0); + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/PictureInPictureParams.java b/java/ql/test/stubs/google-android-9.0.0/android/app/PictureInPictureParams.java new file mode 100644 index 00000000000..4186fa98993 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/PictureInPictureParams.java @@ -0,0 +1,16 @@ +// Generated automatically from android.app.PictureInPictureParams for testing purposes + +package android.app; + +import android.os.Parcel; +import android.os.Parcelable; + +public class PictureInPictureParams implements Parcelable +{ + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public int describeContents(){ return 0; } + public int hashCode(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/PictureInPictureUiState.java b/java/ql/test/stubs/google-android-9.0.0/android/app/PictureInPictureUiState.java new file mode 100644 index 00000000000..9e2485a6f3a --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/PictureInPictureUiState.java @@ -0,0 +1,16 @@ +// Generated automatically from android.app.PictureInPictureUiState for testing purposes + +package android.app; + +import android.os.Parcel; +import android.os.Parcelable; + +public class PictureInPictureUiState implements Parcelable +{ + public boolean equals(Object p0){ return false; } + public boolean isStashed(){ return false; } + public int describeContents(){ return 0; } + public int hashCode(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/RemoteAction.java b/java/ql/test/stubs/google-android-9.0.0/android/app/RemoteAction.java new file mode 100644 index 00000000000..58dec2cc81a --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/RemoteAction.java @@ -0,0 +1,30 @@ +// Generated automatically from android.app.RemoteAction for testing purposes + +package android.app; + +import android.app.PendingIntent; +import android.graphics.drawable.Icon; +import android.os.Parcel; +import android.os.Parcelable; +import java.io.PrintWriter; + +public class RemoteAction implements Parcelable +{ + protected RemoteAction() {} + public CharSequence getContentDescription(){ return null; } + public CharSequence getTitle(){ return null; } + public Icon getIcon(){ return null; } + public PendingIntent getActionIntent(){ return null; } + public RemoteAction clone(){ return null; } + public RemoteAction(Icon p0, CharSequence p1, CharSequence p2, PendingIntent p3){} + public boolean equals(Object p0){ return false; } + public boolean isEnabled(){ return false; } + public boolean shouldShowIcon(){ return false; } + public int describeContents(){ return 0; } + public int hashCode(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void dump(String p0, PrintWriter p1){} + public void setEnabled(boolean p0){} + public void setShouldShowIcon(boolean p0){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/SharedElementCallback.java b/java/ql/test/stubs/google-android-9.0.0/android/app/SharedElementCallback.java new file mode 100644 index 00000000000..1f4cd82b40e --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/SharedElementCallback.java @@ -0,0 +1,27 @@ +// Generated automatically from android.app.SharedElementCallback for testing purposes + +package android.app; + +import android.content.Context; +import android.graphics.Matrix; +import android.graphics.RectF; +import android.os.Parcelable; +import android.view.View; +import java.util.List; +import java.util.Map; + +abstract public class SharedElementCallback +{ + public Parcelable onCaptureSharedElementSnapshot(View p0, Matrix p1, RectF p2){ return null; } + public SharedElementCallback(){} + public View onCreateSnapshotView(Context p0, Parcelable p1){ return null; } + public void onMapSharedElements(List p0, Map p1){} + public void onRejectSharedElements(List p0){} + public void onSharedElementEnd(List p0, List p1, List p2){} + public void onSharedElementStart(List p0, List p1, List p2){} + public void onSharedElementsArrived(List p0, List p1, SharedElementCallback.OnSharedElementsReadyListener p2){} + static public interface OnSharedElementsReadyListener + { + void onSharedElementsReady(); + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/TaskInfo.java b/java/ql/test/stubs/google-android-9.0.0/android/app/TaskInfo.java new file mode 100644 index 00000000000..a5a38ed25bf --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/TaskInfo.java @@ -0,0 +1,21 @@ +// Generated automatically from android.app.TaskInfo for testing purposes + +package android.app; + +import android.app.ActivityManager; +import android.content.ComponentName; +import android.content.Intent; + +public class TaskInfo +{ + public ActivityManager.TaskDescription taskDescription = null; + public ComponentName baseActivity = null; + public ComponentName origActivity = null; + public ComponentName topActivity = null; + public Intent baseIntent = null; + public String toString(){ return null; } + public boolean isRunning = false; + public boolean isVisible(){ return false; } + public int numActivities = 0; + public int taskId = 0; +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/TaskStackBuilder.java b/java/ql/test/stubs/google-android-9.0.0/android/app/TaskStackBuilder.java new file mode 100644 index 00000000000..39544930481 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/TaskStackBuilder.java @@ -0,0 +1,28 @@ +// Generated automatically from android.app.TaskStackBuilder for testing purposes + +package android.app; + +import android.app.Activity; +import android.app.PendingIntent; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; + +public class TaskStackBuilder +{ + protected TaskStackBuilder() {} + public Intent editIntentAt(int p0){ return null; } + public Intent[] getIntents(){ return null; } + public PendingIntent getPendingIntent(int p0, int p1){ return null; } + public PendingIntent getPendingIntent(int p0, int p1, Bundle p2){ return null; } + public TaskStackBuilder addNextIntent(Intent p0){ return null; } + public TaskStackBuilder addNextIntentWithParentStack(Intent p0){ return null; } + public TaskStackBuilder addParentStack(Activity p0){ return null; } + public TaskStackBuilder addParentStack(Class p0){ return null; } + public TaskStackBuilder addParentStack(ComponentName p0){ return null; } + public int getIntentCount(){ return 0; } + public static TaskStackBuilder create(Context p0){ return null; } + public void startActivities(){} + public void startActivities(Bundle p0){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/VoiceInteractor.java b/java/ql/test/stubs/google-android-9.0.0/android/app/VoiceInteractor.java new file mode 100644 index 00000000000..5d3f6f654cd --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/VoiceInteractor.java @@ -0,0 +1,31 @@ +// Generated automatically from android.app.VoiceInteractor for testing purposes + +package android.app; + +import android.app.Activity; +import android.content.Context; +import java.util.concurrent.Executor; + +public class VoiceInteractor +{ + abstract static public class Request + { + public Activity getActivity(){ return null; } + public Context getContext(){ return null; } + public String getName(){ return null; } + public String toString(){ return null; } + public void cancel(){} + public void onAttached(Activity p0){} + public void onCancel(){} + public void onDetached(){} + } + public VoiceInteractor.Request getActiveRequest(String p0){ return null; } + public VoiceInteractor.Request[] getActiveRequests(){ return null; } + public boolean isDestroyed(){ return false; } + public boolean registerOnDestroyedCallback(Executor p0, Runnable p1){ return false; } + public boolean submitRequest(VoiceInteractor.Request p0){ return false; } + public boolean submitRequest(VoiceInteractor.Request p0, String p1){ return false; } + public boolean unregisterOnDestroyedCallback(Runnable p0){ return false; } + public boolean[] supportsCommands(String[] p0){ return null; } + public void notifyDirectActionsChanged(){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/assist/AssistContent.java b/java/ql/test/stubs/google-android-9.0.0/android/app/assist/AssistContent.java new file mode 100644 index 00000000000..49f515177ea --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/assist/AssistContent.java @@ -0,0 +1,29 @@ +// Generated automatically from android.app.assist.AssistContent for testing purposes + +package android.app.assist; + +import android.content.ClipData; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +public class AssistContent implements Parcelable +{ + public AssistContent(){} + public Bundle getExtras(){ return null; } + public ClipData getClipData(){ return null; } + public Intent getIntent(){ return null; } + public String getStructuredData(){ return null; } + public Uri getWebUri(){ return null; } + public boolean isAppProvidedIntent(){ return false; } + public boolean isAppProvidedWebUri(){ return false; } + public int describeContents(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void setClipData(ClipData p0){} + public void setIntent(Intent p0){} + public void setStructuredData(String p0){} + public void setWebUri(Uri p0){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/ComponentCallbacks2.java b/java/ql/test/stubs/google-android-9.0.0/android/content/ComponentCallbacks2.java index d70ac92ec20..f8c83ab104d 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/content/ComponentCallbacks2.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/ComponentCallbacks2.java @@ -2,7 +2,10 @@ package android.content; -public interface ComponentCallbacks2 extends ComponentCallbacks { +import android.content.ComponentCallbacks; + +public interface ComponentCallbacks2 extends ComponentCallbacks +{ static int TRIM_MEMORY_BACKGROUND = 0; static int TRIM_MEMORY_COMPLETE = 0; static int TRIM_MEMORY_MODERATE = 0; @@ -10,6 +13,5 @@ public interface ComponentCallbacks2 extends ComponentCallbacks { static int TRIM_MEMORY_RUNNING_LOW = 0; static int TRIM_MEMORY_RUNNING_MODERATE = 0; static int TRIM_MEMORY_UI_HIDDEN = 0; - void onTrimMemory(int p0); } diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/DialogInterface.java b/java/ql/test/stubs/google-android-9.0.0/android/content/DialogInterface.java new file mode 100644 index 00000000000..d47429790f3 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/DialogInterface.java @@ -0,0 +1,41 @@ +// Generated automatically from android.content.DialogInterface for testing purposes + +package android.content; + +import android.view.KeyEvent; + +public interface DialogInterface +{ + static int BUTTON1 = 0; + static int BUTTON2 = 0; + static int BUTTON3 = 0; + static int BUTTON_NEGATIVE = 0; + static int BUTTON_NEUTRAL = 0; + static int BUTTON_POSITIVE = 0; + static public interface OnCancelListener + { + void onCancel(DialogInterface p0); + } + static public interface OnClickListener + { + void onClick(DialogInterface p0, int p1); + } + static public interface OnDismissListener + { + void onDismiss(DialogInterface p0); + } + static public interface OnKeyListener + { + boolean onKey(DialogInterface p0, int p1, KeyEvent p2); + } + static public interface OnMultiChoiceClickListener + { + void onClick(DialogInterface p0, int p1, boolean p2); + } + static public interface OnShowListener + { + void onShow(DialogInterface p0); + } + void cancel(); + void dismiss(); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/Loader.java b/java/ql/test/stubs/google-android-9.0.0/android/content/Loader.java new file mode 100644 index 00000000000..2b327100cef --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/Loader.java @@ -0,0 +1,51 @@ +// Generated automatically from android.content.Loader for testing purposes + +package android.content; + +import android.content.Context; +import java.io.FileDescriptor; +import java.io.PrintWriter; + +public class Loader +{ + protected Loader() {} + protected boolean onCancelLoad(){ return false; } + protected void onAbandon(){} + protected void onForceLoad(){} + protected void onReset(){} + protected void onStartLoading(){} + protected void onStopLoading(){} + public Context getContext(){ return null; } + public Loader(Context p0){} + public String dataToString(D p0){ return null; } + public String toString(){ return null; } + public boolean cancelLoad(){ return false; } + public boolean isAbandoned(){ return false; } + public boolean isReset(){ return false; } + public boolean isStarted(){ return false; } + public boolean takeContentChanged(){ return false; } + public final void startLoading(){} + public int getId(){ return 0; } + public void abandon(){} + public void commitContentChanged(){} + public void deliverCancellation(){} + public void deliverResult(D p0){} + public void dump(String p0, FileDescriptor p1, PrintWriter p2, String[] p3){} + public void forceLoad(){} + public void onContentChanged(){} + public void registerListener(int p0, Loader.OnLoadCompleteListener p1){} + public void registerOnLoadCanceledListener(Loader.OnLoadCanceledListener p0){} + public void reset(){} + public void rollbackContentChanged(){} + public void stopLoading(){} + public void unregisterListener(Loader.OnLoadCompleteListener p0){} + public void unregisterOnLoadCanceledListener(Loader.OnLoadCanceledListener p0){} + static public interface OnLoadCanceledListener + { + void onLoadCanceled(Loader p0); + } + static public interface OnLoadCompleteListener + { + void onLoadComplete(Loader p0, D p1); + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/media/AudioAttributes.java b/java/ql/test/stubs/google-android-9.0.0/android/media/AudioAttributes.java index 20dff0cc760..042da801af2 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/media/AudioAttributes.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/media/AudioAttributes.java @@ -11,10 +11,12 @@ public class AudioAttributes implements Parcelable public String toString(){ return null; } public boolean areHapticChannelsMuted(){ return false; } public boolean equals(Object p0){ return false; } + public boolean isContentSpatialized(){ return false; } public int describeContents(){ return 0; } public int getAllowedCapturePolicy(){ return 0; } public int getContentType(){ return 0; } public int getFlags(){ return 0; } + public int getSpatializationBehavior(){ return 0; } public int getUsage(){ return 0; } public int getVolumeControlStream(){ return 0; } public int hashCode(){ return 0; } @@ -30,6 +32,8 @@ public class AudioAttributes implements Parcelable public static int FLAG_AUDIBILITY_ENFORCED = 0; public static int FLAG_HW_AV_SYNC = 0; public static int FLAG_LOW_LATENCY = 0; + public static int SPATIALIZATION_BEHAVIOR_AUTO = 0; + public static int SPATIALIZATION_BEHAVIOR_NEVER = 0; public static int USAGE_ALARM = 0; public static int USAGE_ASSISTANCE_ACCESSIBILITY = 0; public static int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 0; diff --git a/java/ql/test/stubs/google-android-9.0.0/android/net/http/SslCertificate.java b/java/ql/test/stubs/google-android-9.0.0/android/net/http/SslCertificate.java new file mode 100644 index 00000000000..8c22fcb0a49 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/net/http/SslCertificate.java @@ -0,0 +1,34 @@ +// Generated automatically from android.net.http.SslCertificate for testing purposes + +package android.net.http; + +import android.os.Bundle; +import java.security.cert.X509Certificate; +import java.util.Date; + +public class SslCertificate +{ + protected SslCertificate() {} + public Date getValidNotAfterDate(){ return null; } + public Date getValidNotBeforeDate(){ return null; } + public SslCertificate(String p0, String p1, Date p2, Date p3){} + public SslCertificate(String p0, String p1, String p2, String p3){} + public SslCertificate(X509Certificate p0){} + public SslCertificate.DName getIssuedBy(){ return null; } + public SslCertificate.DName getIssuedTo(){ return null; } + public String getValidNotAfter(){ return null; } + public String getValidNotBefore(){ return null; } + public String toString(){ return null; } + public X509Certificate getX509Certificate(){ return null; } + public class DName + { + protected DName() {} + public DName(String p0){} + public String getCName(){ return null; } + public String getDName(){ return null; } + public String getOName(){ return null; } + public String getUName(){ return null; } + } + public static Bundle saveState(SslCertificate p0){ return null; } + public static SslCertificate restoreState(Bundle p0){ return null; } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/net/http/SslError.java b/java/ql/test/stubs/google-android-9.0.0/android/net/http/SslError.java new file mode 100644 index 00000000000..62feb4a498d --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/net/http/SslError.java @@ -0,0 +1,28 @@ +// Generated automatically from android.net.http.SslError for testing purposes + +package android.net.http; + +import android.net.http.SslCertificate; +import java.security.cert.X509Certificate; + +public class SslError +{ + protected SslError() {} + public SslCertificate getCertificate(){ return null; } + public SslError(int p0, SslCertificate p1){} + public SslError(int p0, SslCertificate p1, String p2){} + public SslError(int p0, X509Certificate p1){} + public SslError(int p0, X509Certificate p1, String p2){} + public String getUrl(){ return null; } + public String toString(){ return null; } + public boolean addError(int p0){ return false; } + public boolean hasError(int p0){ return false; } + public int getPrimaryError(){ return 0; } + public static int SSL_DATE_INVALID = 0; + public static int SSL_EXPIRED = 0; + public static int SSL_IDMISMATCH = 0; + public static int SSL_INVALID = 0; + public static int SSL_MAX_ERROR = 0; + public static int SSL_NOTYETVALID = 0; + public static int SSL_UNTRUSTED = 0; +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/os/Debug.java b/java/ql/test/stubs/google-android-9.0.0/android/os/Debug.java new file mode 100644 index 00000000000..2ce002f144c --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/os/Debug.java @@ -0,0 +1,110 @@ +// Generated automatically from android.os.Debug for testing purposes + +package android.os; + +import android.os.Parcel; +import android.os.Parcelable; +import java.io.FileDescriptor; +import java.util.Map; + +public class Debug +{ + protected Debug() {} + public static Map getRuntimeStats(){ return null; } + public static String getRuntimeStat(String p0){ return null; } + public static boolean dumpService(String p0, FileDescriptor p1, String[] p2){ return false; } + public static boolean isDebuggerConnected(){ return false; } + public static boolean waitingForDebugger(){ return false; } + public static int SHOW_CLASSLOADER = 0; + public static int SHOW_FULL_DETAIL = 0; + public static int SHOW_INITIALIZED = 0; + public static int TRACE_COUNT_ALLOCS = 0; + public static int getBinderDeathObjectCount(){ return 0; } + public static int getBinderLocalObjectCount(){ return 0; } + public static int getBinderProxyObjectCount(){ return 0; } + public static int getBinderReceivedTransactions(){ return 0; } + public static int getBinderSentTransactions(){ return 0; } + public static int getGlobalAllocCount(){ return 0; } + public static int getGlobalAllocSize(){ return 0; } + public static int getGlobalClassInitCount(){ return 0; } + public static int getGlobalClassInitTime(){ return 0; } + public static int getGlobalExternalAllocCount(){ return 0; } + public static int getGlobalExternalAllocSize(){ return 0; } + public static int getGlobalExternalFreedCount(){ return 0; } + public static int getGlobalExternalFreedSize(){ return 0; } + public static int getGlobalFreedCount(){ return 0; } + public static int getGlobalFreedSize(){ return 0; } + public static int getGlobalGcInvocationCount(){ return 0; } + public static int getLoadedClassCount(){ return 0; } + public static int getThreadAllocCount(){ return 0; } + public static int getThreadAllocSize(){ return 0; } + public static int getThreadExternalAllocCount(){ return 0; } + public static int getThreadExternalAllocSize(){ return 0; } + public static int getThreadGcInvocationCount(){ return 0; } + public static int setAllocationLimit(int p0){ return 0; } + public static int setGlobalAllocationLimit(int p0){ return 0; } + public static long getNativeHeapAllocatedSize(){ return 0; } + public static long getNativeHeapFreeSize(){ return 0; } + public static long getNativeHeapSize(){ return 0; } + public static long getPss(){ return 0; } + public static long threadCpuTimeNanos(){ return 0; } + public static void attachJvmtiAgent(String p0, String p1, ClassLoader p2){} + public static void changeDebugPort(int p0){} + public static void dumpHprofData(String p0){} + public static void enableEmulatorTraceOutput(){} + public static void getMemoryInfo(Debug.MemoryInfo p0){} + public static void printLoadedClasses(int p0){} + public static void resetAllCounts(){} + public static void resetGlobalAllocCount(){} + public static void resetGlobalAllocSize(){} + public static void resetGlobalClassInitCount(){} + public static void resetGlobalClassInitTime(){} + public static void resetGlobalExternalAllocCount(){} + public static void resetGlobalExternalAllocSize(){} + public static void resetGlobalExternalFreedCount(){} + public static void resetGlobalExternalFreedSize(){} + public static void resetGlobalFreedCount(){} + public static void resetGlobalFreedSize(){} + public static void resetGlobalGcInvocationCount(){} + public static void resetThreadAllocCount(){} + public static void resetThreadAllocSize(){} + public static void resetThreadExternalAllocCount(){} + public static void resetThreadExternalAllocSize(){} + public static void resetThreadGcInvocationCount(){} + public static void startAllocCounting(){} + public static void startMethodTracing(){} + public static void startMethodTracing(String p0){} + public static void startMethodTracing(String p0, int p1){} + public static void startMethodTracing(String p0, int p1, int p2){} + public static void startMethodTracingSampling(String p0, int p1, int p2){} + public static void startNativeTracing(){} + public static void stopAllocCounting(){} + public static void stopMethodTracing(){} + public static void stopNativeTracing(){} + public static void waitForDebugger(){} + static public class MemoryInfo implements Parcelable + { + public Map getMemoryStats(){ return null; } + public MemoryInfo(){} + public String getMemoryStat(String p0){ return null; } + public int dalvikPrivateDirty = 0; + public int dalvikPss = 0; + public int dalvikSharedDirty = 0; + public int describeContents(){ return 0; } + public int getTotalPrivateClean(){ return 0; } + public int getTotalPrivateDirty(){ return 0; } + public int getTotalPss(){ return 0; } + public int getTotalSharedClean(){ return 0; } + public int getTotalSharedDirty(){ return 0; } + public int getTotalSwappablePss(){ return 0; } + public int nativePrivateDirty = 0; + public int nativePss = 0; + public int nativeSharedDirty = 0; + public int otherPrivateDirty = 0; + public int otherPss = 0; + public int otherSharedDirty = 0; + public static Parcelable.Creator CREATOR = null; + public void readFromParcel(Parcel p0){} + public void writeToParcel(Parcel p0, int p1){} + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/os/Parcelable.java b/java/ql/test/stubs/google-android-9.0.0/android/os/Parcelable.java index 626061a6799..3aceab4de0a 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/os/Parcelable.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/os/Parcelable.java @@ -2,6 +2,7 @@ package android.os; +import android.app.Fragment; import android.os.Parcel; public interface Parcelable @@ -9,6 +10,10 @@ public interface Parcelable int describeContents(); static int CONTENTS_FILE_DESCRIPTOR = 0; static int PARCELABLE_WRITE_RETURN_VALUE = 0; + static public interface ClassLoaderCreator extends Parcelable.Creator + { + T createFromParcel(Parcel p0, ClassLoader p1); + } static public interface Creator { T createFromParcel(Parcel p0); diff --git a/java/ql/test/stubs/google-android-9.0.0/android/print/PageRange.java b/java/ql/test/stubs/google-android-9.0.0/android/print/PageRange.java new file mode 100644 index 00000000000..7e8a182c81d --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/print/PageRange.java @@ -0,0 +1,21 @@ +// Generated automatically from android.print.PageRange for testing purposes + +package android.print; + +import android.os.Parcel; +import android.os.Parcelable; + +public class PageRange implements Parcelable +{ + protected PageRange() {} + public PageRange(int p0, int p1){} + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public int describeContents(){ return 0; } + public int getEnd(){ return 0; } + public int getStart(){ return 0; } + public int hashCode(){ return 0; } + public static PageRange ALL_PAGES = null; + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/print/PrintAttributes.java b/java/ql/test/stubs/google-android-9.0.0/android/print/PrintAttributes.java new file mode 100644 index 00000000000..82578cf9f76 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/print/PrintAttributes.java @@ -0,0 +1,162 @@ +// Generated automatically from android.print.PrintAttributes for testing purposes + +package android.print; + +import android.content.pm.PackageManager; +import android.os.Parcel; +import android.os.Parcelable; + +public class PrintAttributes implements Parcelable +{ + public PrintAttributes.Margins getMinMargins(){ return null; } + public PrintAttributes.MediaSize getMediaSize(){ return null; } + public PrintAttributes.Resolution getResolution(){ return null; } + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public int describeContents(){ return 0; } + public int getColorMode(){ return 0; } + public int getDuplexMode(){ return 0; } + public int hashCode(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static int COLOR_MODE_COLOR = 0; + public static int COLOR_MODE_MONOCHROME = 0; + public static int DUPLEX_MODE_LONG_EDGE = 0; + public static int DUPLEX_MODE_NONE = 0; + public static int DUPLEX_MODE_SHORT_EDGE = 0; + public void writeToParcel(Parcel p0, int p1){} + static public class Margins + { + protected Margins() {} + public Margins(int p0, int p1, int p2, int p3){} + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public int getBottomMils(){ return 0; } + public int getLeftMils(){ return 0; } + public int getRightMils(){ return 0; } + public int getTopMils(){ return 0; } + public int hashCode(){ return 0; } + public static PrintAttributes.Margins NO_MARGINS = null; + } + static public class MediaSize + { + protected MediaSize() {} + public MediaSize(String p0, String p1, int p2, int p3){} + public PrintAttributes.MediaSize asLandscape(){ return null; } + public PrintAttributes.MediaSize asPortrait(){ return null; } + public String getId(){ return null; } + public String getLabel(PackageManager p0){ return null; } + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public boolean isPortrait(){ return false; } + public int getHeightMils(){ return 0; } + public int getWidthMils(){ return 0; } + public int hashCode(){ return 0; } + public static PrintAttributes.MediaSize ANSI_C = null; + public static PrintAttributes.MediaSize ANSI_D = null; + public static PrintAttributes.MediaSize ANSI_E = null; + public static PrintAttributes.MediaSize ANSI_F = null; + public static PrintAttributes.MediaSize ISO_A0 = null; + public static PrintAttributes.MediaSize ISO_A1 = null; + public static PrintAttributes.MediaSize ISO_A10 = null; + public static PrintAttributes.MediaSize ISO_A2 = null; + public static PrintAttributes.MediaSize ISO_A3 = null; + public static PrintAttributes.MediaSize ISO_A4 = null; + public static PrintAttributes.MediaSize ISO_A5 = null; + public static PrintAttributes.MediaSize ISO_A6 = null; + public static PrintAttributes.MediaSize ISO_A7 = null; + public static PrintAttributes.MediaSize ISO_A8 = null; + public static PrintAttributes.MediaSize ISO_A9 = null; + public static PrintAttributes.MediaSize ISO_B0 = null; + public static PrintAttributes.MediaSize ISO_B1 = null; + public static PrintAttributes.MediaSize ISO_B10 = null; + public static PrintAttributes.MediaSize ISO_B2 = null; + public static PrintAttributes.MediaSize ISO_B3 = null; + public static PrintAttributes.MediaSize ISO_B4 = null; + public static PrintAttributes.MediaSize ISO_B5 = null; + public static PrintAttributes.MediaSize ISO_B6 = null; + public static PrintAttributes.MediaSize ISO_B7 = null; + public static PrintAttributes.MediaSize ISO_B8 = null; + public static PrintAttributes.MediaSize ISO_B9 = null; + public static PrintAttributes.MediaSize ISO_C0 = null; + public static PrintAttributes.MediaSize ISO_C1 = null; + public static PrintAttributes.MediaSize ISO_C10 = null; + public static PrintAttributes.MediaSize ISO_C2 = null; + public static PrintAttributes.MediaSize ISO_C3 = null; + public static PrintAttributes.MediaSize ISO_C4 = null; + public static PrintAttributes.MediaSize ISO_C5 = null; + public static PrintAttributes.MediaSize ISO_C6 = null; + public static PrintAttributes.MediaSize ISO_C7 = null; + public static PrintAttributes.MediaSize ISO_C8 = null; + public static PrintAttributes.MediaSize ISO_C9 = null; + public static PrintAttributes.MediaSize JIS_B0 = null; + public static PrintAttributes.MediaSize JIS_B1 = null; + public static PrintAttributes.MediaSize JIS_B10 = null; + public static PrintAttributes.MediaSize JIS_B2 = null; + public static PrintAttributes.MediaSize JIS_B3 = null; + public static PrintAttributes.MediaSize JIS_B4 = null; + public static PrintAttributes.MediaSize JIS_B5 = null; + public static PrintAttributes.MediaSize JIS_B6 = null; + public static PrintAttributes.MediaSize JIS_B7 = null; + public static PrintAttributes.MediaSize JIS_B8 = null; + public static PrintAttributes.MediaSize JIS_B9 = null; + public static PrintAttributes.MediaSize JIS_EXEC = null; + public static PrintAttributes.MediaSize JPN_CHOU2 = null; + public static PrintAttributes.MediaSize JPN_CHOU3 = null; + public static PrintAttributes.MediaSize JPN_CHOU4 = null; + public static PrintAttributes.MediaSize JPN_HAGAKI = null; + public static PrintAttributes.MediaSize JPN_KAHU = null; + public static PrintAttributes.MediaSize JPN_KAKU2 = null; + public static PrintAttributes.MediaSize JPN_OE_PHOTO_L = null; + public static PrintAttributes.MediaSize JPN_OUFUKU = null; + public static PrintAttributes.MediaSize JPN_YOU4 = null; + public static PrintAttributes.MediaSize NA_ARCH_A = null; + public static PrintAttributes.MediaSize NA_ARCH_B = null; + public static PrintAttributes.MediaSize NA_ARCH_C = null; + public static PrintAttributes.MediaSize NA_ARCH_D = null; + public static PrintAttributes.MediaSize NA_ARCH_E = null; + public static PrintAttributes.MediaSize NA_ARCH_E1 = null; + public static PrintAttributes.MediaSize NA_FOOLSCAP = null; + public static PrintAttributes.MediaSize NA_GOVT_LETTER = null; + public static PrintAttributes.MediaSize NA_INDEX_3X5 = null; + public static PrintAttributes.MediaSize NA_INDEX_4X6 = null; + public static PrintAttributes.MediaSize NA_INDEX_5X8 = null; + public static PrintAttributes.MediaSize NA_JUNIOR_LEGAL = null; + public static PrintAttributes.MediaSize NA_LEDGER = null; + public static PrintAttributes.MediaSize NA_LEGAL = null; + public static PrintAttributes.MediaSize NA_LETTER = null; + public static PrintAttributes.MediaSize NA_MONARCH = null; + public static PrintAttributes.MediaSize NA_QUARTO = null; + public static PrintAttributes.MediaSize NA_SUPER_B = null; + public static PrintAttributes.MediaSize NA_TABLOID = null; + public static PrintAttributes.MediaSize OM_DAI_PA_KAI = null; + public static PrintAttributes.MediaSize OM_JUURO_KU_KAI = null; + public static PrintAttributes.MediaSize OM_PA_KAI = null; + public static PrintAttributes.MediaSize PRC_1 = null; + public static PrintAttributes.MediaSize PRC_10 = null; + public static PrintAttributes.MediaSize PRC_16K = null; + public static PrintAttributes.MediaSize PRC_2 = null; + public static PrintAttributes.MediaSize PRC_3 = null; + public static PrintAttributes.MediaSize PRC_4 = null; + public static PrintAttributes.MediaSize PRC_5 = null; + public static PrintAttributes.MediaSize PRC_6 = null; + public static PrintAttributes.MediaSize PRC_7 = null; + public static PrintAttributes.MediaSize PRC_8 = null; + public static PrintAttributes.MediaSize PRC_9 = null; + public static PrintAttributes.MediaSize ROC_16K = null; + public static PrintAttributes.MediaSize ROC_8K = null; + public static PrintAttributes.MediaSize UNKNOWN_LANDSCAPE = null; + public static PrintAttributes.MediaSize UNKNOWN_PORTRAIT = null; + } + static public class Resolution + { + protected Resolution() {} + public Resolution(String p0, String p1, int p2, int p3){} + public String getId(){ return null; } + public String getLabel(){ return null; } + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public int getHorizontalDpi(){ return 0; } + public int getVerticalDpi(){ return 0; } + public int hashCode(){ return 0; } + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/print/PrintDocumentAdapter.java b/java/ql/test/stubs/google-android-9.0.0/android/print/PrintDocumentAdapter.java new file mode 100644 index 00000000000..bee84a4361f --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/print/PrintDocumentAdapter.java @@ -0,0 +1,32 @@ +// Generated automatically from android.print.PrintDocumentAdapter for testing purposes + +package android.print; + +import android.os.Bundle; +import android.os.CancellationSignal; +import android.os.ParcelFileDescriptor; +import android.print.PageRange; +import android.print.PrintAttributes; +import android.print.PrintDocumentInfo; + +abstract public class PrintDocumentAdapter +{ + abstract static public class LayoutResultCallback + { + public void onLayoutCancelled(){} + public void onLayoutFailed(CharSequence p0){} + public void onLayoutFinished(PrintDocumentInfo p0, boolean p1){} + } + abstract static public class WriteResultCallback + { + public void onWriteCancelled(){} + public void onWriteFailed(CharSequence p0){} + public void onWriteFinished(PageRange[] p0){} + } + public PrintDocumentAdapter(){} + public abstract void onLayout(PrintAttributes p0, PrintAttributes p1, CancellationSignal p2, PrintDocumentAdapter.LayoutResultCallback p3, Bundle p4); + public abstract void onWrite(PageRange[] p0, ParcelFileDescriptor p1, CancellationSignal p2, PrintDocumentAdapter.WriteResultCallback p3); + public static String EXTRA_PRINT_PREVIEW = null; + public void onFinish(){} + public void onStart(){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/print/PrintDocumentInfo.java b/java/ql/test/stubs/google-android-9.0.0/android/print/PrintDocumentInfo.java new file mode 100644 index 00000000000..bc42c86d4f2 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/print/PrintDocumentInfo.java @@ -0,0 +1,25 @@ +// Generated automatically from android.print.PrintDocumentInfo for testing purposes + +package android.print; + +import android.os.Parcel; +import android.os.Parcelable; + +public class PrintDocumentInfo implements Parcelable +{ + protected PrintDocumentInfo() {} + public String getName(){ return null; } + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public int describeContents(){ return 0; } + public int getContentType(){ return 0; } + public int getPageCount(){ return 0; } + public int hashCode(){ return 0; } + public long getDataSize(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static int CONTENT_TYPE_DOCUMENT = 0; + public static int CONTENT_TYPE_PHOTO = 0; + public static int CONTENT_TYPE_UNKNOWN = 0; + public static int PAGE_COUNT_UNKNOWN = 0; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/text/Spannable.java b/java/ql/test/stubs/google-android-9.0.0/android/text/Spannable.java index eb55f4778ef..f615e7328a5 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/text/Spannable.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/text/Spannable.java @@ -6,6 +6,12 @@ import android.text.Spanned; public interface Spannable extends Spanned { + static public class Factory + { + public Factory(){} + public Spannable newSpannable(CharSequence p0){ return null; } + public static Spannable.Factory getInstance(){ return null; } + } void removeSpan(Object p0); void setSpan(Object p0, int p1, int p2, int p3); } diff --git a/java/ql/test/stubs/google-android-9.0.0/android/transition/PathMotion.java b/java/ql/test/stubs/google-android-9.0.0/android/transition/PathMotion.java new file mode 100644 index 00000000000..91b7a1cf016 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/transition/PathMotion.java @@ -0,0 +1,14 @@ +// Generated automatically from android.transition.PathMotion for testing purposes + +package android.transition; + +import android.content.Context; +import android.graphics.Path; +import android.util.AttributeSet; + +abstract public class PathMotion +{ + public PathMotion(){} + public PathMotion(Context p0, AttributeSet p1){} + public abstract Path getPath(float p0, float p1, float p2, float p3); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/transition/Scene.java b/java/ql/test/stubs/google-android-9.0.0/android/transition/Scene.java new file mode 100644 index 00000000000..57fe7cc104e --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/transition/Scene.java @@ -0,0 +1,22 @@ +// Generated automatically from android.transition.Scene for testing purposes + +package android.transition; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; + +public class Scene +{ + protected Scene() {} + public Scene(ViewGroup p0){} + public Scene(ViewGroup p0, View p1){} + public Scene(ViewGroup p0, ViewGroup p1){} + public ViewGroup getSceneRoot(){ return null; } + public static Scene getCurrentScene(ViewGroup p0){ return null; } + public static Scene getSceneForLayout(ViewGroup p0, int p1, Context p2){ return null; } + public void enter(){} + public void exit(){} + public void setEnterAction(Runnable p0){} + public void setExitAction(Runnable p0){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/transition/Transition.java b/java/ql/test/stubs/google-android-9.0.0/android/transition/Transition.java new file mode 100644 index 00000000000..8adeae42c36 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/transition/Transition.java @@ -0,0 +1,83 @@ +// Generated automatically from android.transition.Transition for testing purposes + +package android.transition; + +import android.animation.Animator; +import android.animation.TimeInterpolator; +import android.content.Context; +import android.graphics.Rect; +import android.transition.PathMotion; +import android.transition.TransitionPropagation; +import android.transition.TransitionValues; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import java.util.List; + +abstract public class Transition implements Cloneable +{ + abstract static public class EpicenterCallback + { + public EpicenterCallback(){} + public abstract Rect onGetEpicenter(Transition p0); + } + public Animator createAnimator(ViewGroup p0, TransitionValues p1, TransitionValues p2){ return null; } + public List getTargetTypes(){ return null; } + public List getTargetIds(){ return null; } + public List getTargetNames(){ return null; } + public List getTargets(){ return null; } + public PathMotion getPathMotion(){ return null; } + public Rect getEpicenter(){ return null; } + public String getName(){ return null; } + public String toString(){ return null; } + public String[] getTransitionProperties(){ return null; } + public TimeInterpolator getInterpolator(){ return null; } + public Transition addListener(Transition.TransitionListener p0){ return null; } + public Transition addTarget(Class p0){ return null; } + public Transition addTarget(String p0){ return null; } + public Transition addTarget(View p0){ return null; } + public Transition addTarget(int p0){ return null; } + public Transition clone(){ return null; } + public Transition excludeChildren(Class p0, boolean p1){ return null; } + public Transition excludeChildren(View p0, boolean p1){ return null; } + public Transition excludeChildren(int p0, boolean p1){ return null; } + public Transition excludeTarget(Class p0, boolean p1){ return null; } + public Transition excludeTarget(String p0, boolean p1){ return null; } + public Transition excludeTarget(View p0, boolean p1){ return null; } + public Transition excludeTarget(int p0, boolean p1){ return null; } + public Transition removeListener(Transition.TransitionListener p0){ return null; } + public Transition removeTarget(Class p0){ return null; } + public Transition removeTarget(String p0){ return null; } + public Transition removeTarget(View p0){ return null; } + public Transition removeTarget(int p0){ return null; } + public Transition setDuration(long p0){ return null; } + public Transition setInterpolator(TimeInterpolator p0){ return null; } + public Transition setStartDelay(long p0){ return null; } + public Transition(){} + public Transition(Context p0, AttributeSet p1){} + public Transition.EpicenterCallback getEpicenterCallback(){ return null; } + public TransitionPropagation getPropagation(){ return null; } + public TransitionValues getTransitionValues(View p0, boolean p1){ return null; } + public abstract void captureEndValues(TransitionValues p0); + public abstract void captureStartValues(TransitionValues p0); + public boolean canRemoveViews(){ return false; } + public boolean isTransitionRequired(TransitionValues p0, TransitionValues p1){ return false; } + public long getDuration(){ return 0; } + public long getStartDelay(){ return 0; } + public static int MATCH_ID = 0; + public static int MATCH_INSTANCE = 0; + public static int MATCH_ITEM_ID = 0; + public static int MATCH_NAME = 0; + public void setEpicenterCallback(Transition.EpicenterCallback p0){} + public void setMatchOrder(int... p0){} + public void setPathMotion(PathMotion p0){} + public void setPropagation(TransitionPropagation p0){} + static public interface TransitionListener + { + void onTransitionCancel(Transition p0); + void onTransitionEnd(Transition p0); + void onTransitionPause(Transition p0); + void onTransitionResume(Transition p0); + void onTransitionStart(Transition p0); + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/transition/TransitionManager.java b/java/ql/test/stubs/google-android-9.0.0/android/transition/TransitionManager.java new file mode 100644 index 00000000000..79a91ccabba --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/transition/TransitionManager.java @@ -0,0 +1,20 @@ +// Generated automatically from android.transition.TransitionManager for testing purposes + +package android.transition; + +import android.transition.Scene; +import android.transition.Transition; +import android.view.ViewGroup; + +public class TransitionManager +{ + public TransitionManager(){} + public static void beginDelayedTransition(ViewGroup p0){} + public static void beginDelayedTransition(ViewGroup p0, Transition p1){} + public static void endTransitions(ViewGroup p0){} + public static void go(Scene p0){} + public static void go(Scene p0, Transition p1){} + public void setTransition(Scene p0, Scene p1, Transition p2){} + public void setTransition(Scene p0, Transition p1){} + public void transitionTo(Scene p0){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/transition/TransitionPropagation.java b/java/ql/test/stubs/google-android-9.0.0/android/transition/TransitionPropagation.java new file mode 100644 index 00000000000..0101e4f5cd2 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/transition/TransitionPropagation.java @@ -0,0 +1,15 @@ +// Generated automatically from android.transition.TransitionPropagation for testing purposes + +package android.transition; + +import android.transition.Transition; +import android.transition.TransitionValues; +import android.view.ViewGroup; + +abstract public class TransitionPropagation +{ + public TransitionPropagation(){} + public abstract String[] getPropagationProperties(); + public abstract long getStartDelay(ViewGroup p0, Transition p1, TransitionValues p2, TransitionValues p3); + public abstract void captureValues(TransitionValues p0); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/transition/TransitionValues.java b/java/ql/test/stubs/google-android-9.0.0/android/transition/TransitionValues.java new file mode 100644 index 00000000000..27cbfe690f5 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/transition/TransitionValues.java @@ -0,0 +1,17 @@ +// Generated automatically from android.transition.TransitionValues for testing purposes + +package android.transition; + +import android.view.View; +import java.util.Map; + +public class TransitionValues +{ + public String toString(){ return null; } + public TransitionValues(){} + public TransitionValues(View p0){} + public View view = null; + public boolean equals(Object p0){ return false; } + public final Map values = null; + public int hashCode(){ return 0; } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/AttachedSurfaceControl.java b/java/ql/test/stubs/google-android-9.0.0/android/view/AttachedSurfaceControl.java index 7ac18735ee3..e4140572b6a 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/view/AttachedSurfaceControl.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/AttachedSurfaceControl.java @@ -8,4 +8,11 @@ public interface AttachedSurfaceControl { SurfaceControl.Transaction buildReparentTransaction(SurfaceControl p0); boolean applyTransactionOnDraw(SurfaceControl.Transaction p0); + default int getBufferTransformHint(){ return 0; } + default void addOnBufferTransformHintChangedListener(AttachedSurfaceControl.OnBufferTransformHintChangedListener p0){} + default void removeOnBufferTransformHintChangedListener(AttachedSurfaceControl.OnBufferTransformHintChangedListener p0){} + static public interface OnBufferTransformHintChangedListener + { + void onBufferTransformHintChanged(int p0); + } } diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/DragAndDropPermissions.java b/java/ql/test/stubs/google-android-9.0.0/android/view/DragAndDropPermissions.java new file mode 100644 index 00000000000..c0b47133d07 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/DragAndDropPermissions.java @@ -0,0 +1,15 @@ +// Generated automatically from android.view.DragAndDropPermissions for testing purposes + +package android.view; + +import android.os.Parcel; +import android.os.Parcelable; + +public class DragAndDropPermissions implements Parcelable +{ + protected DragAndDropPermissions() {} + public int describeContents(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void release(){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/FrameMetrics.java b/java/ql/test/stubs/google-android-9.0.0/android/view/FrameMetrics.java new file mode 100644 index 00000000000..98c91865a80 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/FrameMetrics.java @@ -0,0 +1,25 @@ +// Generated automatically from android.view.FrameMetrics for testing purposes + +package android.view; + + +public class FrameMetrics +{ + protected FrameMetrics() {} + public FrameMetrics(FrameMetrics p0){} + public long getMetric(int p0){ return 0; } + public static int ANIMATION_DURATION = 0; + public static int COMMAND_ISSUE_DURATION = 0; + public static int DEADLINE = 0; + public static int DRAW_DURATION = 0; + public static int FIRST_DRAW_FRAME = 0; + public static int GPU_DURATION = 0; + public static int INPUT_HANDLING_DURATION = 0; + public static int INTENDED_VSYNC_TIMESTAMP = 0; + public static int LAYOUT_MEASURE_DURATION = 0; + public static int SWAP_BUFFERS_DURATION = 0; + public static int SYNC_DURATION = 0; + public static int TOTAL_DURATION = 0; + public static int UNKNOWN_DELAY_DURATION = 0; + public static int VSYNC_TIMESTAMP = 0; +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/InputQueue.java b/java/ql/test/stubs/google-android-9.0.0/android/view/InputQueue.java new file mode 100644 index 00000000000..083a7bdb4b1 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/InputQueue.java @@ -0,0 +1,14 @@ +// Generated automatically from android.view.InputQueue for testing purposes + +package android.view; + + +public class InputQueue +{ + protected void finalize(){} + static public interface Callback + { + void onInputQueueCreated(InputQueue p0); + void onInputQueueDestroyed(InputQueue p0); + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/KeyboardShortcutGroup.java b/java/ql/test/stubs/google-android-9.0.0/android/view/KeyboardShortcutGroup.java new file mode 100644 index 00000000000..13f1cdaddc2 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/KeyboardShortcutGroup.java @@ -0,0 +1,21 @@ +// Generated automatically from android.view.KeyboardShortcutGroup for testing purposes + +package android.view; + +import android.os.Parcel; +import android.os.Parcelable; +import android.view.KeyboardShortcutInfo; +import java.util.List; + +public class KeyboardShortcutGroup implements Parcelable +{ + protected KeyboardShortcutGroup() {} + public CharSequence getLabel(){ return null; } + public KeyboardShortcutGroup(CharSequence p0){} + public KeyboardShortcutGroup(CharSequence p0, List p1){} + public List getItems(){ return null; } + public int describeContents(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void addItem(KeyboardShortcutInfo p0){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/KeyboardShortcutInfo.java b/java/ql/test/stubs/google-android-9.0.0/android/view/KeyboardShortcutInfo.java new file mode 100644 index 00000000000..a6d682ce01f --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/KeyboardShortcutInfo.java @@ -0,0 +1,20 @@ +// Generated automatically from android.view.KeyboardShortcutInfo for testing purposes + +package android.view; + +import android.os.Parcel; +import android.os.Parcelable; + +public class KeyboardShortcutInfo implements Parcelable +{ + protected KeyboardShortcutInfo() {} + public CharSequence getLabel(){ return null; } + public KeyboardShortcutInfo(CharSequence p0, char p1, int p2){} + public KeyboardShortcutInfo(CharSequence p0, int p1, int p2){} + public char getBaseCharacter(){ return '0'; } + public int describeContents(){ return 0; } + public int getKeycode(){ return 0; } + public int getModifiers(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/SearchEvent.java b/java/ql/test/stubs/google-android-9.0.0/android/view/SearchEvent.java new file mode 100644 index 00000000000..064261513cf --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/SearchEvent.java @@ -0,0 +1,12 @@ +// Generated automatically from android.view.SearchEvent for testing purposes + +package android.view; + +import android.view.InputDevice; + +public class SearchEvent +{ + protected SearchEvent() {} + public InputDevice getInputDevice(){ return null; } + public SearchEvent(InputDevice p0){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/SurfaceControl.java b/java/ql/test/stubs/google-android-9.0.0/android/view/SurfaceControl.java index 6feff4a32c2..8ea03128948 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/view/SurfaceControl.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/SurfaceControl.java @@ -14,6 +14,12 @@ public class SurfaceControl implements Parcelable public boolean isValid(){ return false; } public int describeContents(){ return 0; } public static Parcelable.Creator CREATOR = null; + public static int BUFFER_TRANSFORM_IDENTITY = 0; + public static int BUFFER_TRANSFORM_MIRROR_HORIZONTAL = 0; + public static int BUFFER_TRANSFORM_MIRROR_VERTICAL = 0; + public static int BUFFER_TRANSFORM_ROTATE_180 = 0; + public static int BUFFER_TRANSFORM_ROTATE_270 = 0; + public static int BUFFER_TRANSFORM_ROTATE_90 = 0; public void readFromParcel(Parcel p0){} public void release(){} public void writeToParcel(Parcel p0, int p1){} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/SurfaceHolder.java b/java/ql/test/stubs/google-android-9.0.0/android/view/SurfaceHolder.java new file mode 100644 index 00000000000..561f640b706 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/SurfaceHolder.java @@ -0,0 +1,40 @@ +// Generated automatically from android.view.SurfaceHolder for testing purposes + +package android.view; + +import android.graphics.Canvas; +import android.graphics.Rect; +import android.view.Surface; + +public interface SurfaceHolder +{ + Canvas lockCanvas(); + Canvas lockCanvas(Rect p0); + Rect getSurfaceFrame(); + Surface getSurface(); + boolean isCreating(); + default Canvas lockHardwareCanvas(){ return null; } + static int SURFACE_TYPE_GPU = 0; + static int SURFACE_TYPE_HARDWARE = 0; + static int SURFACE_TYPE_NORMAL = 0; + static int SURFACE_TYPE_PUSH_BUFFERS = 0; + static public interface Callback + { + void surfaceChanged(SurfaceHolder p0, int p1, int p2, int p3); + void surfaceCreated(SurfaceHolder p0); + void surfaceDestroyed(SurfaceHolder p0); + } + static public interface Callback2 extends SurfaceHolder.Callback + { + default void surfaceRedrawNeededAsync(SurfaceHolder p0, Runnable p1){} + void surfaceRedrawNeeded(SurfaceHolder p0); + } + void addCallback(SurfaceHolder.Callback p0); + void removeCallback(SurfaceHolder.Callback p0); + void setFixedSize(int p0, int p1); + void setFormat(int p0); + void setKeepScreenOn(boolean p0); + void setSizeFromLayout(); + void setType(int p0); + void unlockCanvasAndPost(Canvas p0); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/View.java b/java/ql/test/stubs/google-android-9.0.0/android/view/View.java index 2004c624c6e..e91fa287eb2 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/view/View.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/View.java @@ -525,6 +525,7 @@ public class View implements AccessibilityEventSource, Drawable.Callback, KeyEve public static int AUTOFILL_TYPE_NONE = 0; public static int AUTOFILL_TYPE_TEXT = 0; public static int AUTOFILL_TYPE_TOGGLE = 0; + public static int DRAG_FLAG_ACCESSIBILITY_ACTION = 0; public static int DRAG_FLAG_GLOBAL = 0; public static int DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION = 0; public static int DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION = 0; diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/ViewGroup.java b/java/ql/test/stubs/google-android-9.0.0/android/view/ViewGroup.java index 7e05f4b3781..a90504337ff 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/view/ViewGroup.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/ViewGroup.java @@ -258,6 +258,27 @@ abstract public class ViewGroup extends View implements ViewManager, ViewParent public static int WRAP_CONTENT = 0; public void resolveLayoutDirection(int p0){} } + static public class MarginLayoutParams extends ViewGroup.LayoutParams + { + protected MarginLayoutParams() {} + public MarginLayoutParams(Context p0, AttributeSet p1){} + public MarginLayoutParams(ViewGroup.LayoutParams p0){} + public MarginLayoutParams(ViewGroup.MarginLayoutParams p0){} + public MarginLayoutParams(int p0, int p1){} + public boolean isMarginRelative(){ return false; } + public int bottomMargin = 0; + public int getLayoutDirection(){ return 0; } + public int getMarginEnd(){ return 0; } + public int getMarginStart(){ return 0; } + public int leftMargin = 0; + public int rightMargin = 0; + public int topMargin = 0; + public void resolveLayoutDirection(int p0){} + public void setLayoutDirection(int p0){} + public void setMarginEnd(int p0){} + public void setMarginStart(int p0){} + public void setMargins(int p0, int p1, int p2, int p3){} + } static public interface OnHierarchyChangeListener { void onChildViewAdded(View p0, View p1); diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/Window.java b/java/ql/test/stubs/google-android-9.0.0/android/view/Window.java new file mode 100644 index 00000000000..13f5e79ca4a --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/Window.java @@ -0,0 +1,251 @@ +// Generated automatically from android.view.Window for testing purposes + +package android.view; + +import android.content.Context; +import android.content.res.Configuration; +import android.content.res.TypedArray; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.media.session.MediaController; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.transition.Scene; +import android.transition.Transition; +import android.transition.TransitionManager; +import android.view.ActionMode; +import android.view.AttachedSurfaceControl; +import android.view.FrameMetrics; +import android.view.InputEvent; +import android.view.InputQueue; +import android.view.KeyEvent; +import android.view.KeyboardShortcutGroup; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.ScrollCaptureCallback; +import android.view.SearchEvent; +import android.view.SurfaceHolder; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowInsetsController; +import android.view.WindowManager; +import android.view.accessibility.AccessibilityEvent; +import java.util.List; + +abstract public class Window +{ + protected Window() {} + protected abstract void onActive(); + protected final boolean hasSoftInputMode(){ return false; } + protected final int getFeatures(){ return 0; } + protected final int getForcedWindowFlags(){ return 0; } + protected final int getLocalFeatures(){ return 0; } + protected static int DEFAULT_FEATURES = 0; + protected void setDefaultWindowFormat(int p0){} + public T findViewById(int p0){ return null; } + public AttachedSurfaceControl getRootSurfaceControl(){ return null; } + public List getSystemGestureExclusionRects(){ return null; } + public MediaController getMediaController(){ return null; } + public Scene getContentScene(){ return null; } + public Transition getEnterTransition(){ return null; } + public Transition getExitTransition(){ return null; } + public Transition getReenterTransition(){ return null; } + public Transition getReturnTransition(){ return null; } + public Transition getSharedElementEnterTransition(){ return null; } + public Transition getSharedElementExitTransition(){ return null; } + public Transition getSharedElementReenterTransition(){ return null; } + public Transition getSharedElementReturnTransition(){ return null; } + public TransitionManager getTransitionManager(){ return null; } + public Window(Context p0){} + public WindowInsetsController getInsetsController(){ return null; } + public WindowManager getWindowManager(){ return null; } + public abstract Bundle saveHierarchyState(); + public abstract LayoutInflater getLayoutInflater(); + public abstract View getCurrentFocus(); + public abstract View getDecorView(); + public abstract View peekDecorView(); + public abstract boolean isFloating(); + public abstract boolean isShortcutKey(int p0, KeyEvent p1); + public abstract boolean performContextMenuIdentifierAction(int p0, int p1); + public abstract boolean performPanelIdentifierAction(int p0, int p1, int p2); + public abstract boolean performPanelShortcut(int p0, int p1, KeyEvent p2, int p3); + public abstract boolean superDispatchGenericMotionEvent(MotionEvent p0); + public abstract boolean superDispatchKeyEvent(KeyEvent p0); + public abstract boolean superDispatchKeyShortcutEvent(KeyEvent p0); + public abstract boolean superDispatchTouchEvent(MotionEvent p0); + public abstract boolean superDispatchTrackballEvent(MotionEvent p0); + public abstract int getNavigationBarColor(); + public abstract int getStatusBarColor(); + public abstract int getVolumeControlStream(); + public abstract void addContentView(View p0, ViewGroup.LayoutParams p1); + public abstract void closeAllPanels(); + public abstract void closePanel(int p0); + public abstract void invalidatePanelMenu(int p0); + public abstract void onConfigurationChanged(Configuration p0); + public abstract void openPanel(int p0, KeyEvent p1); + public abstract void restoreHierarchyState(Bundle p0); + public abstract void setBackgroundDrawable(Drawable p0); + public abstract void setChildDrawable(int p0, Drawable p1); + public abstract void setChildInt(int p0, int p1); + public abstract void setContentView(View p0); + public abstract void setContentView(View p0, ViewGroup.LayoutParams p1); + public abstract void setContentView(int p0); + public abstract void setDecorCaptionShade(int p0); + public abstract void setFeatureDrawable(int p0, Drawable p1); + public abstract void setFeatureDrawableAlpha(int p0, int p1); + public abstract void setFeatureDrawableResource(int p0, int p1); + public abstract void setFeatureDrawableUri(int p0, Uri p1); + public abstract void setFeatureInt(int p0, int p1); + public abstract void setNavigationBarColor(int p0); + public abstract void setResizingCaptionDrawable(Drawable p0); + public abstract void setStatusBarColor(int p0); + public abstract void setTitle(CharSequence p0); + public abstract void setTitleColor(int p0); + public abstract void setVolumeControlStream(int p0); + public abstract void takeInputQueue(InputQueue.Callback p0); + public abstract void takeKeyEvents(boolean p0); + public abstract void takeSurface(SurfaceHolder.Callback2 p0); + public abstract void togglePanel(int p0, KeyEvent p1); + public boolean getAllowEnterTransitionOverlap(){ return false; } + public boolean getAllowReturnTransitionOverlap(){ return false; } + public boolean getSharedElementsUseOverlay(){ return false; } + public boolean hasFeature(int p0){ return false; } + public boolean isNavigationBarContrastEnforced(){ return false; } + public boolean isStatusBarContrastEnforced(){ return false; } + public boolean isWideColorGamut(){ return false; } + public boolean requestFeature(int p0){ return false; } + public final T requireViewById(int p0){ return null; } + public final Context getContext(){ return null; } + public final TypedArray getWindowStyle(){ return null; } + public final Window getContainer(){ return null; } + public final Window.Callback getCallback(){ return null; } + public final WindowManager.LayoutParams getAttributes(){ return null; } + public final boolean hasChildren(){ return false; } + public final boolean isActive(){ return false; } + public final void addOnFrameMetricsAvailableListener(Window.OnFrameMetricsAvailableListener p0, Handler p1){} + public final void makeActive(){} + public final void removeOnFrameMetricsAvailableListener(Window.OnFrameMetricsAvailableListener p0){} + public final void setHideOverlayWindows(boolean p0){} + public final void setRestrictedCaptionAreaListener(Window.OnRestrictedCaptionAreaChangedListener p0){} + public int getColorMode(){ return 0; } + public int getNavigationBarDividerColor(){ return 0; } + public long getTransitionBackgroundFadeDuration(){ return 0; } + public static String NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME = null; + public static String STATUS_BAR_BACKGROUND_TRANSITION_NAME = null; + public static int DECOR_CAPTION_SHADE_AUTO = 0; + public static int DECOR_CAPTION_SHADE_DARK = 0; + public static int DECOR_CAPTION_SHADE_LIGHT = 0; + public static int FEATURE_ACTION_BAR = 0; + public static int FEATURE_ACTION_BAR_OVERLAY = 0; + public static int FEATURE_ACTION_MODE_OVERLAY = 0; + public static int FEATURE_ACTIVITY_TRANSITIONS = 0; + public static int FEATURE_CONTENT_TRANSITIONS = 0; + public static int FEATURE_CONTEXT_MENU = 0; + public static int FEATURE_CUSTOM_TITLE = 0; + public static int FEATURE_INDETERMINATE_PROGRESS = 0; + public static int FEATURE_LEFT_ICON = 0; + public static int FEATURE_NO_TITLE = 0; + public static int FEATURE_OPTIONS_PANEL = 0; + public static int FEATURE_PROGRESS = 0; + public static int FEATURE_RIGHT_ICON = 0; + public static int FEATURE_SWIPE_TO_DISMISS = 0; + public static int ID_ANDROID_CONTENT = 0; + public static int PROGRESS_END = 0; + public static int PROGRESS_INDETERMINATE_OFF = 0; + public static int PROGRESS_INDETERMINATE_ON = 0; + public static int PROGRESS_SECONDARY_END = 0; + public static int PROGRESS_SECONDARY_START = 0; + public static int PROGRESS_START = 0; + public static int PROGRESS_VISIBILITY_OFF = 0; + public static int PROGRESS_VISIBILITY_ON = 0; + public static int getDefaultFeatures(Context p0){ return 0; } + public void addFlags(int p0){} + public void clearFlags(int p0){} + public void injectInputEvent(InputEvent p0){} + public void registerScrollCaptureCallback(ScrollCaptureCallback p0){} + public void setAllowEnterTransitionOverlap(boolean p0){} + public void setAllowReturnTransitionOverlap(boolean p0){} + public void setAttributes(WindowManager.LayoutParams p0){} + public void setBackgroundBlurRadius(int p0){} + public void setBackgroundDrawableResource(int p0){} + public void setCallback(Window.Callback p0){} + public void setClipToOutline(boolean p0){} + public void setColorMode(int p0){} + public void setContainer(Window p0){} + public void setDecorFitsSystemWindows(boolean p0){} + public void setDimAmount(float p0){} + public void setElevation(float p0){} + public void setEnterTransition(Transition p0){} + public void setExitTransition(Transition p0){} + public void setFlags(int p0, int p1){} + public void setFormat(int p0){} + public void setGravity(int p0){} + public void setIcon(int p0){} + public void setLayout(int p0, int p1){} + public void setLocalFocus(boolean p0, boolean p1){} + public void setLogo(int p0){} + public void setMediaController(MediaController p0){} + public void setNavigationBarContrastEnforced(boolean p0){} + public void setNavigationBarDividerColor(int p0){} + public void setPreferMinimalPostProcessing(boolean p0){} + public void setReenterTransition(Transition p0){} + public void setReturnTransition(Transition p0){} + public void setSharedElementEnterTransition(Transition p0){} + public void setSharedElementExitTransition(Transition p0){} + public void setSharedElementReenterTransition(Transition p0){} + public void setSharedElementReturnTransition(Transition p0){} + public void setSharedElementsUseOverlay(boolean p0){} + public void setSoftInputMode(int p0){} + public void setStatusBarContrastEnforced(boolean p0){} + public void setSustainedPerformanceMode(boolean p0){} + public void setSystemGestureExclusionRects(List p0){} + public void setTransitionBackgroundFadeDuration(long p0){} + public void setTransitionManager(TransitionManager p0){} + public void setType(int p0){} + public void setUiOptions(int p0){} + public void setUiOptions(int p0, int p1){} + public void setWindowAnimations(int p0){} + public void setWindowManager(WindowManager p0, IBinder p1, String p2){} + public void setWindowManager(WindowManager p0, IBinder p1, String p2, boolean p3){} + public void unregisterScrollCaptureCallback(ScrollCaptureCallback p0){} + static public interface Callback + { + ActionMode onWindowStartingActionMode(ActionMode.Callback p0); + ActionMode onWindowStartingActionMode(ActionMode.Callback p0, int p1); + View onCreatePanelView(int p0); + boolean dispatchGenericMotionEvent(MotionEvent p0); + boolean dispatchKeyEvent(KeyEvent p0); + boolean dispatchKeyShortcutEvent(KeyEvent p0); + boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent p0); + boolean dispatchTouchEvent(MotionEvent p0); + boolean dispatchTrackballEvent(MotionEvent p0); + boolean onCreatePanelMenu(int p0, Menu p1); + boolean onMenuItemSelected(int p0, MenuItem p1); + boolean onMenuOpened(int p0, Menu p1); + boolean onPreparePanel(int p0, View p1, Menu p2); + boolean onSearchRequested(); + boolean onSearchRequested(SearchEvent p0); + default void onPointerCaptureChanged(boolean p0){} + default void onProvideKeyboardShortcuts(List p0, Menu p1, int p2){} + void onActionModeFinished(ActionMode p0); + void onActionModeStarted(ActionMode p0); + void onAttachedToWindow(); + void onContentChanged(); + void onDetachedFromWindow(); + void onPanelClosed(int p0, Menu p1); + void onWindowAttributesChanged(WindowManager.LayoutParams p0); + void onWindowFocusChanged(boolean p0); + } + static public interface OnFrameMetricsAvailableListener + { + void onFrameMetricsAvailable(Window p0, FrameMetrics p1, int p2); + } + static public interface OnRestrictedCaptionAreaChangedListener + { + void onRestrictedCaptionAreaChanged(Rect p0); + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/WindowManager.java b/java/ql/test/stubs/google-android-9.0.0/android/view/WindowManager.java new file mode 100644 index 00000000000..ea0c98476ab --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/WindowManager.java @@ -0,0 +1,183 @@ +// Generated automatically from android.view.WindowManager for testing purposes + +package android.view; + +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; +import android.view.Display; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewManager; +import android.view.WindowMetrics; +import java.util.concurrent.Executor; +import java.util.function.Consumer; + +public interface WindowManager extends ViewManager +{ + Display getDefaultDisplay(); + default WindowMetrics getCurrentWindowMetrics(){ return null; } + default WindowMetrics getMaximumWindowMetrics(){ return null; } + default boolean isCrossWindowBlurEnabled(){ return false; } + default void addCrossWindowBlurEnabledListener(Consumer p0){} + default void addCrossWindowBlurEnabledListener(Executor p0, Consumer p1){} + default void removeCrossWindowBlurEnabledListener(Consumer p0){} + static public class LayoutParams extends ViewGroup.LayoutParams implements Parcelable + { + public IBinder token = null; + public LayoutParams(){} + public LayoutParams(Parcel p0){} + public LayoutParams(int p0){} + public LayoutParams(int p0, int p1){} + public LayoutParams(int p0, int p1, int p2){} + public LayoutParams(int p0, int p1, int p2, int p3, int p4){} + public LayoutParams(int p0, int p1, int p2, int p3, int p4, int p5, int p6){} + public String debug(String p0){ return null; } + public String packageName = null; + public String toString(){ return null; } + public boolean isFitInsetsIgnoringVisibility(){ return false; } + public boolean preferMinimalPostProcessing = false; + public final CharSequence getTitle(){ return null; } + public final int copyFrom(WindowManager.LayoutParams p0){ return 0; } + public final void setTitle(CharSequence p0){} + public float alpha = 0; + public float buttonBrightness = 0; + public float dimAmount = 0; + public float horizontalMargin = 0; + public float horizontalWeight = 0; + public float preferredRefreshRate = 0; + public float screenBrightness = 0; + public float verticalMargin = 0; + public float verticalWeight = 0; + public int describeContents(){ return 0; } + public int flags = 0; + public int format = 0; + public int getBlurBehindRadius(){ return 0; } + public int getColorMode(){ return 0; } + public int getFitInsetsSides(){ return 0; } + public int getFitInsetsTypes(){ return 0; } + public int gravity = 0; + public int layoutInDisplayCutoutMode = 0; + public int memoryType = 0; + public int preferredDisplayModeId = 0; + public int rotationAnimation = 0; + public int screenOrientation = 0; + public int softInputMode = 0; + public int systemUiVisibility = 0; + public int type = 0; + public int windowAnimations = 0; + public int x = 0; + public int y = 0; + public static Parcelable.Creator CREATOR = null; + public static boolean mayUseInputMethod(int p0){ return false; } + public static float BRIGHTNESS_OVERRIDE_FULL = 0; + public static float BRIGHTNESS_OVERRIDE_NONE = 0; + public static float BRIGHTNESS_OVERRIDE_OFF = 0; + public static int ALPHA_CHANGED = 0; + public static int ANIMATION_CHANGED = 0; + public static int DIM_AMOUNT_CHANGED = 0; + public static int FIRST_APPLICATION_WINDOW = 0; + public static int FIRST_SUB_WINDOW = 0; + public static int FIRST_SYSTEM_WINDOW = 0; + public static int FLAGS_CHANGED = 0; + public static int FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0; + public static int FLAG_ALT_FOCUSABLE_IM = 0; + public static int FLAG_BLUR_BEHIND = 0; + public static int FLAG_DIM_BEHIND = 0; + public static int FLAG_DISMISS_KEYGUARD = 0; + public static int FLAG_DITHER = 0; + public static int FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS = 0; + public static int FLAG_FORCE_NOT_FULLSCREEN = 0; + public static int FLAG_FULLSCREEN = 0; + public static int FLAG_HARDWARE_ACCELERATED = 0; + public static int FLAG_IGNORE_CHEEK_PRESSES = 0; + public static int FLAG_KEEP_SCREEN_ON = 0; + public static int FLAG_LAYOUT_ATTACHED_IN_DECOR = 0; + public static int FLAG_LAYOUT_INSET_DECOR = 0; + public static int FLAG_LAYOUT_IN_OVERSCAN = 0; + public static int FLAG_LAYOUT_IN_SCREEN = 0; + public static int FLAG_LAYOUT_NO_LIMITS = 0; + public static int FLAG_LOCAL_FOCUS_MODE = 0; + public static int FLAG_NOT_FOCUSABLE = 0; + public static int FLAG_NOT_TOUCHABLE = 0; + public static int FLAG_NOT_TOUCH_MODAL = 0; + public static int FLAG_SCALED = 0; + public static int FLAG_SECURE = 0; + public static int FLAG_SHOW_WALLPAPER = 0; + public static int FLAG_SHOW_WHEN_LOCKED = 0; + public static int FLAG_SPLIT_TOUCH = 0; + public static int FLAG_TOUCHABLE_WHEN_WAKING = 0; + public static int FLAG_TRANSLUCENT_NAVIGATION = 0; + public static int FLAG_TRANSLUCENT_STATUS = 0; + public static int FLAG_TURN_SCREEN_ON = 0; + public static int FLAG_WATCH_OUTSIDE_TOUCH = 0; + public static int FORMAT_CHANGED = 0; + public static int LAST_APPLICATION_WINDOW = 0; + public static int LAST_SUB_WINDOW = 0; + public static int LAST_SYSTEM_WINDOW = 0; + public static int LAYOUT_CHANGED = 0; + public static int LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS = 0; + public static int LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT = 0; + public static int LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER = 0; + public static int LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES = 0; + public static int MEMORY_TYPE_CHANGED = 0; + public static int MEMORY_TYPE_GPU = 0; + public static int MEMORY_TYPE_HARDWARE = 0; + public static int MEMORY_TYPE_NORMAL = 0; + public static int MEMORY_TYPE_PUSH_BUFFERS = 0; + public static int ROTATION_ANIMATION_CHANGED = 0; + public static int ROTATION_ANIMATION_CROSSFADE = 0; + public static int ROTATION_ANIMATION_JUMPCUT = 0; + public static int ROTATION_ANIMATION_ROTATE = 0; + public static int ROTATION_ANIMATION_SEAMLESS = 0; + public static int SCREEN_BRIGHTNESS_CHANGED = 0; + public static int SCREEN_ORIENTATION_CHANGED = 0; + public static int SOFT_INPUT_ADJUST_NOTHING = 0; + public static int SOFT_INPUT_ADJUST_PAN = 0; + public static int SOFT_INPUT_ADJUST_RESIZE = 0; + public static int SOFT_INPUT_ADJUST_UNSPECIFIED = 0; + public static int SOFT_INPUT_IS_FORWARD_NAVIGATION = 0; + public static int SOFT_INPUT_MASK_ADJUST = 0; + public static int SOFT_INPUT_MASK_STATE = 0; + public static int SOFT_INPUT_MODE_CHANGED = 0; + public static int SOFT_INPUT_STATE_ALWAYS_HIDDEN = 0; + public static int SOFT_INPUT_STATE_ALWAYS_VISIBLE = 0; + public static int SOFT_INPUT_STATE_HIDDEN = 0; + public static int SOFT_INPUT_STATE_UNCHANGED = 0; + public static int SOFT_INPUT_STATE_UNSPECIFIED = 0; + public static int SOFT_INPUT_STATE_VISIBLE = 0; + public static int TITLE_CHANGED = 0; + public static int TYPE_ACCESSIBILITY_OVERLAY = 0; + public static int TYPE_APPLICATION = 0; + public static int TYPE_APPLICATION_ATTACHED_DIALOG = 0; + public static int TYPE_APPLICATION_MEDIA = 0; + public static int TYPE_APPLICATION_OVERLAY = 0; + public static int TYPE_APPLICATION_PANEL = 0; + public static int TYPE_APPLICATION_STARTING = 0; + public static int TYPE_APPLICATION_SUB_PANEL = 0; + public static int TYPE_BASE_APPLICATION = 0; + public static int TYPE_CHANGED = 0; + public static int TYPE_DRAWN_APPLICATION = 0; + public static int TYPE_INPUT_METHOD = 0; + public static int TYPE_INPUT_METHOD_DIALOG = 0; + public static int TYPE_KEYGUARD_DIALOG = 0; + public static int TYPE_PHONE = 0; + public static int TYPE_PRIORITY_PHONE = 0; + public static int TYPE_PRIVATE_PRESENTATION = 0; + public static int TYPE_SEARCH_BAR = 0; + public static int TYPE_STATUS_BAR = 0; + public static int TYPE_SYSTEM_ALERT = 0; + public static int TYPE_SYSTEM_DIALOG = 0; + public static int TYPE_SYSTEM_ERROR = 0; + public static int TYPE_SYSTEM_OVERLAY = 0; + public static int TYPE_TOAST = 0; + public static int TYPE_WALLPAPER = 0; + public void setBlurBehindRadius(int p0){} + public void setColorMode(int p0){} + public void setFitInsetsIgnoringVisibility(boolean p0){} + public void setFitInsetsSides(int p0){} + public void setFitInsetsTypes(int p0){} + public void writeToParcel(Parcel p0, int p1){} + } + void removeViewImmediate(View p0); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/WindowMetrics.java b/java/ql/test/stubs/google-android-9.0.0/android/view/WindowMetrics.java new file mode 100644 index 00000000000..9c9bc92058c --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/WindowMetrics.java @@ -0,0 +1,14 @@ +// Generated automatically from android.view.WindowMetrics for testing purposes + +package android.view; + +import android.graphics.Rect; +import android.view.WindowInsets; + +public class WindowMetrics +{ + protected WindowMetrics() {} + public Rect getBounds(){ return null; } + public WindowInsets getWindowInsets(){ return null; } + public WindowMetrics(Rect p0, WindowInsets p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/accessibility/AccessibilityEvent.java b/java/ql/test/stubs/google-android-9.0.0/android/view/accessibility/AccessibilityEvent.java index 1db7545960f..5e702246cb9 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/view/accessibility/AccessibilityEvent.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/accessibility/AccessibilityEvent.java @@ -28,6 +28,9 @@ public class AccessibilityEvent extends AccessibilityRecord implements Parcelabl public static Parcelable.Creator CREATOR = null; public static String eventTypeToString(int p0){ return null; } public static int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 0; + public static int CONTENT_CHANGE_TYPE_DRAG_CANCELLED = 0; + public static int CONTENT_CHANGE_TYPE_DRAG_DROPPED = 0; + public static int CONTENT_CHANGE_TYPE_DRAG_STARTED = 0; public static int CONTENT_CHANGE_TYPE_PANE_APPEARED = 0; public static int CONTENT_CHANGE_TYPE_PANE_DISAPPEARED = 0; public static int CONTENT_CHANGE_TYPE_PANE_TITLE = 0; diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/accessibility/AccessibilityNodeInfo.java b/java/ql/test/stubs/google-android-9.0.0/android/view/accessibility/AccessibilityNodeInfo.java index 9724111622d..f781f0a6092 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/view/accessibility/AccessibilityNodeInfo.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/accessibility/AccessibilityNodeInfo.java @@ -230,6 +230,9 @@ public class AccessibilityNodeInfo implements Parcelable public static AccessibilityNodeInfo.AccessibilityAction ACTION_COPY = null; public static AccessibilityNodeInfo.AccessibilityAction ACTION_CUT = null; public static AccessibilityNodeInfo.AccessibilityAction ACTION_DISMISS = null; + public static AccessibilityNodeInfo.AccessibilityAction ACTION_DRAG_CANCEL = null; + public static AccessibilityNodeInfo.AccessibilityAction ACTION_DRAG_DROP = null; + public static AccessibilityNodeInfo.AccessibilityAction ACTION_DRAG_START = null; public static AccessibilityNodeInfo.AccessibilityAction ACTION_EXPAND = null; public static AccessibilityNodeInfo.AccessibilityAction ACTION_FOCUS = null; public static AccessibilityNodeInfo.AccessibilityAction ACTION_HIDE_TOOLTIP = null; diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/ConversationAction.java b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/ConversationAction.java new file mode 100644 index 00000000000..fc35651b52d --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/ConversationAction.java @@ -0,0 +1,31 @@ +// Generated automatically from android.view.textclassifier.ConversationAction for testing purposes + +package android.view.textclassifier; + +import android.app.RemoteAction; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +public class ConversationAction implements Parcelable +{ + protected ConversationAction() {} + public Bundle getExtras(){ return null; } + public CharSequence getTextReply(){ return null; } + public RemoteAction getAction(){ return null; } + public String getType(){ return null; } + public float getConfidenceScore(){ return 0; } + public int describeContents(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static String TYPE_CALL_PHONE = null; + public static String TYPE_CREATE_REMINDER = null; + public static String TYPE_OPEN_URL = null; + public static String TYPE_SEND_EMAIL = null; + public static String TYPE_SEND_SMS = null; + public static String TYPE_SHARE_LOCATION = null; + public static String TYPE_TEXT_REPLY = null; + public static String TYPE_TRACK_FLIGHT = null; + public static String TYPE_VIEW_CALENDAR = null; + public static String TYPE_VIEW_MAP = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/ConversationActions.java b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/ConversationActions.java new file mode 100644 index 00000000000..ebe2eac2fd2 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/ConversationActions.java @@ -0,0 +1,51 @@ +// Generated automatically from android.view.textclassifier.ConversationActions for testing purposes + +package android.view.textclassifier; + +import android.app.Person; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.view.textclassifier.ConversationAction; +import android.view.textclassifier.TextClassifier; +import java.time.ZonedDateTime; +import java.util.List; + +public class ConversationActions implements Parcelable +{ + protected ConversationActions() {} + public ConversationActions(List p0, String p1){} + public List getConversationActions(){ return null; } + public String getId(){ return null; } + public int describeContents(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} + static public class Message implements Parcelable + { + protected Message() {} + public Bundle getExtras(){ return null; } + public CharSequence getText(){ return null; } + public Person getAuthor(){ return null; } + public ZonedDateTime getReferenceTime(){ return null; } + public int describeContents(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static Person PERSON_USER_OTHERS = null; + public static Person PERSON_USER_SELF = null; + public void writeToParcel(Parcel p0, int p1){} + } + static public class Request implements Parcelable + { + protected Request() {} + public Bundle getExtras(){ return null; } + public List getConversation(){ return null; } + public List getHints(){ return null; } + public String getCallingPackageName(){ return null; } + public TextClassifier.EntityConfig getTypeConfig(){ return null; } + public int describeContents(){ return 0; } + public int getMaxSuggestions(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static String HINT_FOR_IN_APP = null; + public static String HINT_FOR_NOTIFICATION = null; + public void writeToParcel(Parcel p0, int p1){} + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/SelectionEvent.java b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/SelectionEvent.java new file mode 100644 index 00000000000..70d75c390b8 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/SelectionEvent.java @@ -0,0 +1,61 @@ +// Generated automatically from android.view.textclassifier.SelectionEvent for testing purposes + +package android.view.textclassifier; + +import android.os.Parcel; +import android.os.Parcelable; +import android.view.textclassifier.TextClassification; +import android.view.textclassifier.TextClassificationSessionId; +import android.view.textclassifier.TextSelection; + +public class SelectionEvent implements Parcelable +{ + public String getEntityType(){ return null; } + public String getPackageName(){ return null; } + public String getResultId(){ return null; } + public String getWidgetType(){ return null; } + public String getWidgetVersion(){ return null; } + public String toString(){ return null; } + public TextClassificationSessionId getSessionId(){ return null; } + public boolean equals(Object p0){ return false; } + public int describeContents(){ return 0; } + public int getEnd(){ return 0; } + public int getEventIndex(){ return 0; } + public int getEventType(){ return 0; } + public int getInvocationMethod(){ return 0; } + public int getSmartEnd(){ return 0; } + public int getSmartStart(){ return 0; } + public int getStart(){ return 0; } + public int hashCode(){ return 0; } + public long getDurationSincePreviousEvent(){ return 0; } + public long getDurationSinceSessionStart(){ return 0; } + public long getEventTime(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static SelectionEvent createSelectionActionEvent(int p0, int p1, int p2){ return null; } + public static SelectionEvent createSelectionActionEvent(int p0, int p1, int p2, TextClassification p3){ return null; } + public static SelectionEvent createSelectionModifiedEvent(int p0, int p1){ return null; } + public static SelectionEvent createSelectionModifiedEvent(int p0, int p1, TextClassification p2){ return null; } + public static SelectionEvent createSelectionModifiedEvent(int p0, int p1, TextSelection p2){ return null; } + public static SelectionEvent createSelectionStartedEvent(int p0, int p1){ return null; } + public static boolean isTerminal(int p0){ return false; } + public static int ACTION_ABANDON = 0; + public static int ACTION_COPY = 0; + public static int ACTION_CUT = 0; + public static int ACTION_DRAG = 0; + public static int ACTION_OTHER = 0; + public static int ACTION_OVERTYPE = 0; + public static int ACTION_PASTE = 0; + public static int ACTION_RESET = 0; + public static int ACTION_SELECT_ALL = 0; + public static int ACTION_SHARE = 0; + public static int ACTION_SMART_SHARE = 0; + public static int EVENT_AUTO_SELECTION = 0; + public static int EVENT_SELECTION_MODIFIED = 0; + public static int EVENT_SELECTION_STARTED = 0; + public static int EVENT_SMART_SELECTION_MULTI = 0; + public static int EVENT_SMART_SELECTION_SINGLE = 0; + public static int INVOCATION_LINK = 0; + public static int INVOCATION_MANUAL = 0; + public static int INVOCATION_UNKNOWN = 0; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextClassification.java b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextClassification.java new file mode 100644 index 00000000000..bd89b26c523 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextClassification.java @@ -0,0 +1,48 @@ +// Generated automatically from android.view.textclassifier.TextClassification for testing purposes + +package android.view.textclassifier; + +import android.app.RemoteAction; +import android.content.Intent; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.os.LocaleList; +import android.os.Parcel; +import android.os.Parcelable; +import android.view.View; +import java.time.ZonedDateTime; +import java.util.List; + +public class TextClassification implements Parcelable +{ + protected TextClassification() {} + public Bundle getExtras(){ return null; } + public CharSequence getLabel(){ return null; } + public Drawable getIcon(){ return null; } + public Intent getIntent(){ return null; } + public List getActions(){ return null; } + public String getEntity(int p0){ return null; } + public String getId(){ return null; } + public String getText(){ return null; } + public String toString(){ return null; } + public View.OnClickListener getOnClickListener(){ return null; } + public float getConfidenceScore(String p0){ return 0; } + public int describeContents(){ return 0; } + public int getEntityCount(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} + static public class Request implements Parcelable + { + protected Request() {} + public Bundle getExtras(){ return null; } + public CharSequence getText(){ return null; } + public LocaleList getDefaultLocales(){ return null; } + public String getCallingPackageName(){ return null; } + public ZonedDateTime getReferenceTime(){ return null; } + public int describeContents(){ return 0; } + public int getEndIndex(){ return 0; } + public int getStartIndex(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextClassificationContext.java b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextClassificationContext.java new file mode 100644 index 00000000000..a7ab6240ede --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextClassificationContext.java @@ -0,0 +1,18 @@ +// Generated automatically from android.view.textclassifier.TextClassificationContext for testing purposes + +package android.view.textclassifier; + +import android.os.Parcel; +import android.os.Parcelable; + +public class TextClassificationContext implements Parcelable +{ + protected TextClassificationContext() {} + public String getPackageName(){ return null; } + public String getWidgetType(){ return null; } + public String getWidgetVersion(){ return null; } + public String toString(){ return null; } + public int describeContents(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextClassificationSessionId.java b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextClassificationSessionId.java new file mode 100644 index 00000000000..fda8b2a0449 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextClassificationSessionId.java @@ -0,0 +1,17 @@ +// Generated automatically from android.view.textclassifier.TextClassificationSessionId for testing purposes + +package android.view.textclassifier; + +import android.os.Parcel; +import android.os.Parcelable; + +public class TextClassificationSessionId implements Parcelable +{ + public String getValue(){ return null; } + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public int describeContents(){ return 0; } + public int hashCode(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextClassifier.java b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextClassifier.java new file mode 100644 index 00000000000..9f3daa67ea1 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextClassifier.java @@ -0,0 +1,68 @@ +// Generated automatically from android.view.textclassifier.TextClassifier for testing purposes + +package android.view.textclassifier; + +import android.os.LocaleList; +import android.os.Parcel; +import android.os.Parcelable; +import android.view.textclassifier.ConversationActions; +import android.view.textclassifier.SelectionEvent; +import android.view.textclassifier.TextClassification; +import android.view.textclassifier.TextClassifierEvent; +import android.view.textclassifier.TextLanguage; +import android.view.textclassifier.TextLinks; +import android.view.textclassifier.TextSelection; +import java.util.Collection; + +public interface TextClassifier +{ + default ConversationActions suggestConversationActions(ConversationActions.Request p0){ return null; } + default TextClassification classifyText(CharSequence p0, int p1, int p2, LocaleList p3){ return null; } + default TextClassification classifyText(TextClassification.Request p0){ return null; } + default TextLanguage detectLanguage(TextLanguage.Request p0){ return null; } + default TextLinks generateLinks(TextLinks.Request p0){ return null; } + default TextSelection suggestSelection(CharSequence p0, int p1, int p2, LocaleList p3){ return null; } + default TextSelection suggestSelection(TextSelection.Request p0){ return null; } + default boolean isDestroyed(){ return false; } + default int getMaxGenerateLinksTextLength(){ return 0; } + default void destroy(){} + default void onSelectionEvent(SelectionEvent p0){} + default void onTextClassifierEvent(TextClassifierEvent p0){} + static String EXTRA_FROM_TEXT_CLASSIFIER = null; + static String HINT_TEXT_IS_EDITABLE = null; + static String HINT_TEXT_IS_NOT_EDITABLE = null; + static String TYPE_ADDRESS = null; + static String TYPE_DATE = null; + static String TYPE_DATE_TIME = null; + static String TYPE_EMAIL = null; + static String TYPE_FLIGHT_NUMBER = null; + static String TYPE_OTHER = null; + static String TYPE_PHONE = null; + static String TYPE_UNKNOWN = null; + static String TYPE_URL = null; + static String WIDGET_TYPE_CLIPBOARD = null; + static String WIDGET_TYPE_CUSTOM_EDITTEXT = null; + static String WIDGET_TYPE_CUSTOM_TEXTVIEW = null; + static String WIDGET_TYPE_CUSTOM_UNSELECTABLE_TEXTVIEW = null; + static String WIDGET_TYPE_EDITTEXT = null; + static String WIDGET_TYPE_EDIT_WEBVIEW = null; + static String WIDGET_TYPE_NOTIFICATION = null; + static String WIDGET_TYPE_TEXTVIEW = null; + static String WIDGET_TYPE_UNKNOWN = null; + static String WIDGET_TYPE_UNSELECTABLE_TEXTVIEW = null; + static String WIDGET_TYPE_WEBVIEW = null; + static TextClassifier NO_OP = null; + static public class EntityConfig implements Parcelable + { + protected EntityConfig() {} + public Collection getHints(){ return null; } + public Collection resolveEntityListModifications(Collection p0){ return null; } + public boolean shouldIncludeTypesFromTextClassifier(){ return false; } + public int describeContents(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static TextClassifier.EntityConfig create(Collection p0, Collection p1, Collection p2){ return null; } + public static TextClassifier.EntityConfig createWithExplicitEntityList(Collection p0){ return null; } + public static TextClassifier.EntityConfig createWithHints(Collection p0){ return null; } + public void writeToParcel(Parcel p0, int p1){} + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextClassifierEvent.java b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextClassifierEvent.java new file mode 100644 index 00000000000..ad18e8b78f5 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextClassifierEvent.java @@ -0,0 +1,54 @@ +// Generated automatically from android.view.textclassifier.TextClassifierEvent for testing purposes + +package android.view.textclassifier; + +import android.icu.util.ULocale; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.view.textclassifier.TextClassificationContext; + +abstract public class TextClassifierEvent implements Parcelable +{ + protected TextClassifierEvent() {} + public Bundle getExtras(){ return null; } + public String getModelName(){ return null; } + public String getResultId(){ return null; } + public String toString(){ return null; } + public String[] getEntityTypes(){ return null; } + public TextClassificationContext getEventContext(){ return null; } + public ULocale getLocale(){ return null; } + public float[] getScores(){ return null; } + public int describeContents(){ return 0; } + public int getEventCategory(){ return 0; } + public int getEventIndex(){ return 0; } + public int getEventType(){ return 0; } + public int[] getActionIndices(){ return null; } + public static Parcelable.Creator CREATOR = null; + public static int CATEGORY_CONVERSATION_ACTIONS = 0; + public static int CATEGORY_LANGUAGE_DETECTION = 0; + public static int CATEGORY_LINKIFY = 0; + public static int CATEGORY_SELECTION = 0; + public static int TYPE_ACTIONS_GENERATED = 0; + public static int TYPE_ACTIONS_SHOWN = 0; + public static int TYPE_AUTO_SELECTION = 0; + public static int TYPE_COPY_ACTION = 0; + public static int TYPE_CUT_ACTION = 0; + public static int TYPE_LINKS_GENERATED = 0; + public static int TYPE_LINK_CLICKED = 0; + public static int TYPE_MANUAL_REPLY = 0; + public static int TYPE_OTHER_ACTION = 0; + public static int TYPE_OVERTYPE = 0; + public static int TYPE_PASTE_ACTION = 0; + public static int TYPE_SELECTION_DESTROYED = 0; + public static int TYPE_SELECTION_DRAG = 0; + public static int TYPE_SELECTION_MODIFIED = 0; + public static int TYPE_SELECTION_RESET = 0; + public static int TYPE_SELECTION_STARTED = 0; + public static int TYPE_SELECT_ALL = 0; + public static int TYPE_SHARE_ACTION = 0; + public static int TYPE_SMART_ACTION = 0; + public static int TYPE_SMART_SELECTION_MULTI = 0; + public static int TYPE_SMART_SELECTION_SINGLE = 0; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextLanguage.java b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextLanguage.java new file mode 100644 index 00000000000..8c04c6b43df --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextLanguage.java @@ -0,0 +1,32 @@ +// Generated automatically from android.view.textclassifier.TextLanguage for testing purposes + +package android.view.textclassifier; + +import android.icu.util.ULocale; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +public class TextLanguage implements Parcelable +{ + protected TextLanguage() {} + public Bundle getExtras(){ return null; } + public String getId(){ return null; } + public String toString(){ return null; } + public ULocale getLocale(int p0){ return null; } + public float getConfidenceScore(ULocale p0){ return 0; } + public int describeContents(){ return 0; } + public int getLocaleHypothesisCount(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} + static public class Request implements Parcelable + { + protected Request() {} + public Bundle getExtras(){ return null; } + public CharSequence getText(){ return null; } + public String getCallingPackageName(){ return null; } + public int describeContents(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextLinks.java b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextLinks.java index 597d751996e..1e36d672717 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextLinks.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextLinks.java @@ -3,11 +3,14 @@ package android.view.textclassifier; import android.os.Bundle; +import android.os.LocaleList; import android.os.Parcel; import android.os.Parcelable; import android.text.Spannable; import android.text.style.ClickableSpan; import android.view.View; +import android.view.textclassifier.TextClassifier; +import java.time.ZonedDateTime; import java.util.Collection; import java.util.function.Function; @@ -29,6 +32,19 @@ public class TextLinks implements Parcelable public static int STATUS_NO_LINKS_FOUND = 0; public static int STATUS_UNSUPPORTED_CHARACTER = 0; public void writeToParcel(Parcel p0, int p1){} + static public class Request implements Parcelable + { + protected Request() {} + public Bundle getExtras(){ return null; } + public CharSequence getText(){ return null; } + public LocaleList getDefaultLocales(){ return null; } + public String getCallingPackageName(){ return null; } + public TextClassifier.EntityConfig getEntityConfig(){ return null; } + public ZonedDateTime getReferenceTime(){ return null; } + public int describeContents(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} + } static public class TextLink implements Parcelable { protected TextLink() {} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextSelection.java b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextSelection.java new file mode 100644 index 00000000000..015715305c4 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextSelection.java @@ -0,0 +1,40 @@ +// Generated automatically from android.view.textclassifier.TextSelection for testing purposes + +package android.view.textclassifier; + +import android.os.Bundle; +import android.os.LocaleList; +import android.os.Parcel; +import android.os.Parcelable; +import android.view.textclassifier.TextClassification; + +public class TextSelection implements Parcelable +{ + protected TextSelection() {} + public Bundle getExtras(){ return null; } + public String getEntity(int p0){ return null; } + public String getId(){ return null; } + public String toString(){ return null; } + public TextClassification getTextClassification(){ return null; } + public float getConfidenceScore(String p0){ return 0; } + public int describeContents(){ return 0; } + public int getEntityCount(){ return 0; } + public int getSelectionEndIndex(){ return 0; } + public int getSelectionStartIndex(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} + static public class Request implements Parcelable + { + protected Request() {} + public Bundle getExtras(){ return null; } + public CharSequence getText(){ return null; } + public LocaleList getDefaultLocales(){ return null; } + public String getCallingPackageName(){ return null; } + public boolean shouldIncludeTextClassification(){ return false; } + public int describeContents(){ return 0; } + public int getEndIndex(){ return 0; } + public int getStartIndex(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/ClientCertRequest.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/ClientCertRequest.java new file mode 100644 index 00000000000..0af0587e29d --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/ClientCertRequest.java @@ -0,0 +1,19 @@ +// Generated automatically from android.webkit.ClientCertRequest for testing purposes + +package android.webkit; + +import java.security.Principal; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; + +abstract public class ClientCertRequest +{ + public ClientCertRequest(){} + public abstract Principal[] getPrincipals(); + public abstract String getHost(); + public abstract String[] getKeyTypes(); + public abstract int getPort(); + public abstract void cancel(); + public abstract void ignore(); + public abstract void proceed(PrivateKey p0, X509Certificate[] p1); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/ConsoleMessage.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/ConsoleMessage.java new file mode 100644 index 00000000000..9b50c6f7e6e --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/ConsoleMessage.java @@ -0,0 +1,19 @@ +// Generated automatically from android.webkit.ConsoleMessage for testing purposes + +package android.webkit; + + +public class ConsoleMessage +{ + protected ConsoleMessage() {} + public ConsoleMessage(String p0, String p1, int p2, ConsoleMessage.MessageLevel p3){} + public ConsoleMessage.MessageLevel messageLevel(){ return null; } + public String message(){ return null; } + public String sourceId(){ return null; } + public int lineNumber(){ return 0; } + static public enum MessageLevel + { + DEBUG, ERROR, LOG, TIP, WARNING; + private MessageLevel() {} + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/DownloadListener.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/DownloadListener.java new file mode 100644 index 00000000000..2620faf369c --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/DownloadListener.java @@ -0,0 +1,9 @@ +// Generated automatically from android.webkit.DownloadListener for testing purposes + +package android.webkit; + + +public interface DownloadListener +{ + void onDownloadStart(String p0, String p1, String p2, String p3, long p4); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/GeolocationPermissions.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/GeolocationPermissions.java new file mode 100644 index 00000000000..8edb6b2f3dd --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/GeolocationPermissions.java @@ -0,0 +1,20 @@ +// Generated automatically from android.webkit.GeolocationPermissions for testing purposes + +package android.webkit; + +import android.webkit.ValueCallback; +import java.util.Set; + +public class GeolocationPermissions +{ + public static GeolocationPermissions getInstance(){ return null; } + public void allow(String p0){} + public void clear(String p0){} + public void clearAll(){} + public void getAllowed(String p0, ValueCallback p1){} + public void getOrigins(ValueCallback> p0){} + static public interface Callback + { + void invoke(String p0, boolean p1, boolean p2); + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/HttpAuthHandler.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/HttpAuthHandler.java new file mode 100644 index 00000000000..c2bde7e3ffc --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/HttpAuthHandler.java @@ -0,0 +1,12 @@ +// Generated automatically from android.webkit.HttpAuthHandler for testing purposes + +package android.webkit; + +import android.os.Handler; + +public class HttpAuthHandler extends Handler +{ + public boolean useHttpAuthUsernamePassword(){ return false; } + public void cancel(){} + public void proceed(String p0, String p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/JsPromptResult.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/JsPromptResult.java new file mode 100644 index 00000000000..835d76105a0 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/JsPromptResult.java @@ -0,0 +1,10 @@ +// Generated automatically from android.webkit.JsPromptResult for testing purposes + +package android.webkit; + +import android.webkit.JsResult; + +public class JsPromptResult extends JsResult +{ + public void confirm(String p0){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/JsResult.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/JsResult.java new file mode 100644 index 00000000000..c8168438130 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/JsResult.java @@ -0,0 +1,10 @@ +// Generated automatically from android.webkit.JsResult for testing purposes + +package android.webkit; + + +public class JsResult +{ + public final void cancel(){} + public final void confirm(){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/PermissionRequest.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/PermissionRequest.java new file mode 100644 index 00000000000..da64b442084 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/PermissionRequest.java @@ -0,0 +1,18 @@ +// Generated automatically from android.webkit.PermissionRequest for testing purposes + +package android.webkit; + +import android.net.Uri; + +abstract public class PermissionRequest +{ + public PermissionRequest(){} + public abstract String[] getResources(); + public abstract Uri getOrigin(); + public abstract void deny(); + public abstract void grant(String[] p0); + public static String RESOURCE_AUDIO_CAPTURE = null; + public static String RESOURCE_MIDI_SYSEX = null; + public static String RESOURCE_PROTECTED_MEDIA_ID = null; + public static String RESOURCE_VIDEO_CAPTURE = null; +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/RenderProcessGoneDetail.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/RenderProcessGoneDetail.java new file mode 100644 index 00000000000..1e2086fdf3b --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/RenderProcessGoneDetail.java @@ -0,0 +1,11 @@ +// Generated automatically from android.webkit.RenderProcessGoneDetail for testing purposes + +package android.webkit; + + +abstract public class RenderProcessGoneDetail +{ + public RenderProcessGoneDetail(){} + public abstract boolean didCrash(); + public abstract int rendererPriorityAtExit(); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/SafeBrowsingResponse.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/SafeBrowsingResponse.java new file mode 100644 index 00000000000..751d7e76530 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/SafeBrowsingResponse.java @@ -0,0 +1,12 @@ +// Generated automatically from android.webkit.SafeBrowsingResponse for testing purposes + +package android.webkit; + + +abstract public class SafeBrowsingResponse +{ + public SafeBrowsingResponse(){} + public abstract void backToSafety(boolean p0); + public abstract void proceed(boolean p0); + public abstract void showInterstitial(boolean p0); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/SslErrorHandler.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/SslErrorHandler.java new file mode 100644 index 00000000000..ab56fed23fa --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/SslErrorHandler.java @@ -0,0 +1,11 @@ +// Generated automatically from android.webkit.SslErrorHandler for testing purposes + +package android.webkit; + +import android.os.Handler; + +public class SslErrorHandler extends Handler +{ + public void cancel(){} + public void proceed(){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/ValueCallback.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/ValueCallback.java new file mode 100644 index 00000000000..0cdf8831825 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/ValueCallback.java @@ -0,0 +1,9 @@ +// Generated automatically from android.webkit.ValueCallback for testing purposes + +package android.webkit; + + +public interface ValueCallback +{ + void onReceiveValue(T p0); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebBackForwardList.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebBackForwardList.java new file mode 100644 index 00000000000..4fe7956b562 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebBackForwardList.java @@ -0,0 +1,16 @@ +// Generated automatically from android.webkit.WebBackForwardList for testing purposes + +package android.webkit; + +import android.webkit.WebHistoryItem; +import java.io.Serializable; + +abstract public class WebBackForwardList implements Cloneable, Serializable +{ + protected abstract WebBackForwardList clone(); + public WebBackForwardList(){} + public abstract WebHistoryItem getCurrentItem(); + public abstract WebHistoryItem getItemAtIndex(int p0); + public abstract int getCurrentIndex(); + public abstract int getSize(); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebChromeClient.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebChromeClient.java new file mode 100644 index 00000000000..75b1f938d2d --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebChromeClient.java @@ -0,0 +1,67 @@ +// Generated automatically from android.webkit.WebChromeClient for testing purposes + +package android.webkit; + +import android.content.Intent; +import android.graphics.Bitmap; +import android.net.Uri; +import android.os.Message; +import android.view.View; +import android.webkit.ConsoleMessage; +import android.webkit.GeolocationPermissions; +import android.webkit.JsPromptResult; +import android.webkit.JsResult; +import android.webkit.PermissionRequest; +import android.webkit.ValueCallback; +import android.webkit.WebStorage; +import android.webkit.WebView; + +public class WebChromeClient +{ + abstract static public class FileChooserParams + { + public FileChooserParams(){} + public abstract CharSequence getTitle(); + public abstract Intent createIntent(); + public abstract String getFilenameHint(); + public abstract String[] getAcceptTypes(); + public abstract boolean isCaptureEnabled(); + public abstract int getMode(); + public static Uri[] parseResult(int p0, Intent p1){ return null; } + public static int MODE_OPEN = 0; + public static int MODE_OPEN_MULTIPLE = 0; + public static int MODE_SAVE = 0; + } + public Bitmap getDefaultVideoPoster(){ return null; } + public View getVideoLoadingProgressView(){ return null; } + public WebChromeClient(){} + public boolean onConsoleMessage(ConsoleMessage p0){ return false; } + public boolean onCreateWindow(WebView p0, boolean p1, boolean p2, Message p3){ return false; } + public boolean onJsAlert(WebView p0, String p1, String p2, JsResult p3){ return false; } + public boolean onJsBeforeUnload(WebView p0, String p1, String p2, JsResult p3){ return false; } + public boolean onJsConfirm(WebView p0, String p1, String p2, JsResult p3){ return false; } + public boolean onJsPrompt(WebView p0, String p1, String p2, String p3, JsPromptResult p4){ return false; } + public boolean onJsTimeout(){ return false; } + public boolean onShowFileChooser(WebView p0, ValueCallback p1, WebChromeClient.FileChooserParams p2){ return false; } + public void getVisitedHistory(ValueCallback p0){} + public void onCloseWindow(WebView p0){} + public void onConsoleMessage(String p0, int p1, String p2){} + public void onExceededDatabaseQuota(String p0, String p1, long p2, long p3, long p4, WebStorage.QuotaUpdater p5){} + public void onGeolocationPermissionsHidePrompt(){} + public void onGeolocationPermissionsShowPrompt(String p0, GeolocationPermissions.Callback p1){} + public void onHideCustomView(){} + public void onPermissionRequest(PermissionRequest p0){} + public void onPermissionRequestCanceled(PermissionRequest p0){} + public void onProgressChanged(WebView p0, int p1){} + public void onReachedMaxAppCacheSize(long p0, long p1, WebStorage.QuotaUpdater p2){} + public void onReceivedIcon(WebView p0, Bitmap p1){} + public void onReceivedTitle(WebView p0, String p1){} + public void onReceivedTouchIconUrl(WebView p0, String p1, boolean p2){} + public void onRequestFocus(WebView p0){} + public void onShowCustomView(View p0, WebChromeClient.CustomViewCallback p1){} + public void onShowCustomView(View p0, int p1, WebChromeClient.CustomViewCallback p2){} + static public interface CustomViewCallback + { + void onCustomViewHidden(); + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebHistoryItem.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebHistoryItem.java new file mode 100644 index 00000000000..637467d888f --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebHistoryItem.java @@ -0,0 +1,15 @@ +// Generated automatically from android.webkit.WebHistoryItem for testing purposes + +package android.webkit; + +import android.graphics.Bitmap; + +abstract public class WebHistoryItem implements Cloneable +{ + protected abstract WebHistoryItem clone(); + public WebHistoryItem(){} + public abstract Bitmap getFavicon(); + public abstract String getOriginalUrl(); + public abstract String getTitle(); + public abstract String getUrl(); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebMessage.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebMessage.java new file mode 100644 index 00000000000..6e02214c1df --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebMessage.java @@ -0,0 +1,14 @@ +// Generated automatically from android.webkit.WebMessage for testing purposes + +package android.webkit; + +import android.webkit.WebMessagePort; + +public class WebMessage +{ + protected WebMessage() {} + public String getData(){ return null; } + public WebMessage(String p0){} + public WebMessage(String p0, WebMessagePort[] p1){} + public WebMessagePort[] getPorts(){ return null; } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebMessagePort.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebMessagePort.java new file mode 100644 index 00000000000..05ece05e0a3 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebMessagePort.java @@ -0,0 +1,19 @@ +// Generated automatically from android.webkit.WebMessagePort for testing purposes + +package android.webkit; + +import android.os.Handler; +import android.webkit.WebMessage; + +abstract public class WebMessagePort +{ + abstract static public class WebMessageCallback + { + public WebMessageCallback(){} + public void onMessage(WebMessagePort p0, WebMessage p1){} + } + public abstract void close(); + public abstract void postMessage(WebMessage p0); + public abstract void setWebMessageCallback(WebMessagePort.WebMessageCallback p0); + public abstract void setWebMessageCallback(WebMessagePort.WebMessageCallback p0, Handler p1); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebResourceError.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebResourceError.java new file mode 100644 index 00000000000..115aaff3dec --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebResourceError.java @@ -0,0 +1,10 @@ +// Generated automatically from android.webkit.WebResourceError for testing purposes + +package android.webkit; + + +abstract public class WebResourceError +{ + public abstract CharSequence getDescription(); + public abstract int getErrorCode(); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebResourceRequest.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebResourceRequest.java index 9732ca67ae0..7a195a1518a 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebResourceRequest.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebResourceRequest.java @@ -1,72 +1,16 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Generated automatically from android.webkit.WebResourceRequest for testing purposes + package android.webkit; import android.net.Uri; import java.util.Map; -/** - * Encompasses parameters to the {@link WebViewClient#shouldInterceptRequest} - * method. - */ -public interface WebResourceRequest { - /** - * Gets the URL for which the resource request was made. - * - * @return the URL for which the resource request was made. - */ - Uri getUrl(); - - /** - * Gets whether the request was made for the main frame. - * - * @return whether the request was made for the main frame. Will be - * {@code false} for iframes, for example. - */ - boolean isForMainFrame(); - - /** - * Gets whether the request was a result of a server-side redirect. - * - * @return whether the request was a result of a server-side redirect. - */ - boolean isRedirect(); - - /** - * Gets whether a gesture (such as a click) was associated with the request. For - * security reasons in certain situations this method may return {@code false} - * even though the sequence of events which caused the request to be created was - * initiated by a user gesture. - * - * @return whether a gesture was associated with the request. - */ - boolean hasGesture(); - - /** - * Gets the method associated with the request, for example "GET". - * - * @return the method associated with the request. - */ - String getMethod(); - - /** - * Gets the headers associated with the request. These are represented as a - * mapping of header name to header value. - * - * @return the headers associated with the request. - */ +public interface WebResourceRequest +{ Map getRequestHeaders(); -} \ No newline at end of file + String getMethod(); + Uri getUrl(); + boolean hasGesture(); + boolean isForMainFrame(); + boolean isRedirect(); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebResourceResponse.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebResourceResponse.java index 1a2ff3cc1da..9e0e0225644 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebResourceResponse.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebResourceResponse.java @@ -1,173 +1,24 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Generated automatically from android.webkit.WebResourceResponse for testing purposes + package android.webkit; import java.io.InputStream; import java.util.Map; -/** - * Encapsulates a resource response. Applications can return an instance of this - * class from {@link WebViewClient#shouldInterceptRequest} to provide a custom - * response when the WebView requests a particular resource. - */ -public class WebResourceResponse { - /** - * Constructs a resource response with the given MIME type, encoding, and input - * stream. Callers must implement {@link InputStream#read(byte[]) - * InputStream.read(byte[])} for the input stream. - * - * @param mimeType the resource response's MIME type, for example text/html - * @param encoding the resource response's encoding - * @param data the input stream that provides the resource response's data. - * Must not be a StringBufferInputStream. - */ - public WebResourceResponse(String mimeType, String encoding, InputStream data) { - } - - /** - * Constructs a resource response with the given parameters. Callers must - * implement {@link InputStream#read(byte[]) InputStream.read(byte[])} for the - * input stream. - * - * @param mimeType the resource response's MIME type, for example - * text/html - * @param encoding the resource response's encoding - * @param statusCode the status code needs to be in the ranges [100, 299], - * [400, 599]. Causing a redirect by specifying a 3xx - * code is not supported. - * @param reasonPhrase the phrase describing the status code, for example - * "OK". Must be non-empty. - * @param responseHeaders the resource response's headers represented as a - * mapping of header name -> header value. - * @param data the input stream that provides the resource response's - * data. Must not be a StringBufferInputStream. - */ - public WebResourceResponse(String mimeType, String encoding, int statusCode, String reasonPhrase, - Map responseHeaders, InputStream data) { - } - - /** - * Sets the resource response's MIME type, for example "text/html". - * - * @param mimeType The resource response's MIME type - */ - public void setMimeType(String mimeType) { - } - - /** - * Gets the resource response's MIME type. - * - * @return The resource response's MIME type - */ - public String getMimeType() { - return null; - } - - /** - * Sets the resource response's encoding, for example "UTF-8". This is - * used to decode the data from the input stream. - * - * @param encoding The resource response's encoding - */ - public void setEncoding(String encoding) { - } - - /** - * Gets the resource response's encoding. - * - * @return The resource response's encoding - */ - public String getEncoding() { - return null; - } - - /** - * Sets the resource response's status code and reason phrase. - * - * @param statusCode the status code needs to be in the ranges [100, 299], - * [400, 599]. Causing a redirect by specifying a 3xx code - * is not supported. - * @param reasonPhrase the phrase describing the status code, for example "OK". - * Must be non-empty. - */ - public void setStatusCodeAndReasonPhrase(int statusCode, String reasonPhrase) { - } - - /** - * Gets the resource response's status code. - * - * @return The resource response's status code. - */ - public int getStatusCode() { - return -1; - } - - /** - * Gets the description of the resource response's status code. - * - * @return The description of the resource response's status code. - */ - public String getReasonPhrase() { - return null; - } - - /** - * Sets the headers for the resource response. - * - * @param headers Mapping of header name -> header value. - */ - public void setResponseHeaders(Map headers) { - } - - /** - * Gets the headers for the resource response. - * - * @return The headers for the resource response. - */ - public Map getResponseHeaders() { - return null; - } - - /** - * Sets the input stream that provides the resource response's data. Callers - * must implement {@link InputStream#read(byte[]) InputStream.read(byte[])}. - * - * @param data the input stream that provides the resource response's data. Must - * not be a StringBufferInputStream. - */ - public void setData(InputStream data) { - } - - /** - * Gets the input stream that provides the resource response's data. - * - * @return The input stream that provides the resource response's data - */ - public InputStream getData() { - return null; - } - - /** - * The internal version of the constructor that doesn't perform arguments - * checks. - * - * @hide - */ - public WebResourceResponse(boolean immutable, String mimeType, String encoding, int statusCode, String reasonPhrase, - Map responseHeaders, InputStream data) { - } - -} \ No newline at end of file +public class WebResourceResponse +{ + protected WebResourceResponse() {} + public InputStream getData(){ return null; } + public Map getResponseHeaders(){ return null; } + public String getEncoding(){ return null; } + public String getMimeType(){ return null; } + public String getReasonPhrase(){ return null; } + public WebResourceResponse(String p0, String p1, InputStream p2){} + public WebResourceResponse(String p0, String p1, int p2, String p3, Map p4, InputStream p5){} + public int getStatusCode(){ return 0; } + public void setData(InputStream p0){} + public void setEncoding(String p0){} + public void setMimeType(String p0){} + public void setResponseHeaders(Map p0){} + public void setStatusCodeAndReasonPhrase(int p0, String p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebSettings.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebSettings.java index 33c9a1b8a57..60d01fe2b97 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebSettings.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebSettings.java @@ -1,1379 +1,150 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Generated automatically from android.webkit.WebSettings for testing purposes + package android.webkit; -import java.net.CookieManager; import android.content.Context; -/** - * Manages settings state for a WebView. When a WebView is first created, it - * obtains a set of default settings. These default settings will be returned - * from any getter call. A {@code WebSettings} object obtained from - * {@link WebView#getSettings()} is tied to the life of the WebView. If a - * WebView has been destroyed, any method call on {@code WebSettings} will throw - * an {@link IllegalStateException}. - */ -// This is an abstract base class: concrete WebViewProviders must -// create a class derived from this, and return an instance of it in the -// WebViewProvider.getWebSettingsProvider() method implementation. -public abstract class WebSettings { - /** - * Enum for controlling the layout of html. - *
      - *
    • {@code NORMAL} means no rendering changes. This is the recommended choice - * for maximum compatibility across different platforms and Android - * versions.
    • - *
    • {@code SINGLE_COLUMN} moves all content into one column that is the width - * of the view.
    • - *
    • {@code NARROW_COLUMNS} makes all columns no wider than the screen if - * possible. Only use this for API levels prior to - * {@link android.os.Build.VERSION_CODES#KITKAT}.
    • - *
    • {@code TEXT_AUTOSIZING} boosts font size of paragraphs based on - * heuristics to make the text readable when viewing a wide-viewport layout in - * the overview mode. It is recommended to enable zoom support - * {@link #setSupportZoom} when using this mode. Supported from API level - * {@link android.os.Build.VERSION_CODES#KITKAT}
    • - *
    - */ - // XXX: These must match LayoutAlgorithm in Settings.h in WebCore. - public enum LayoutAlgorithm { - NORMAL, - /** - * @deprecated This algorithm is now obsolete. - */ - @Deprecated - SINGLE_COLUMN, - /** - * @deprecated This algorithm is now obsolete. - */ - @Deprecated - NARROW_COLUMNS, TEXT_AUTOSIZING - } - - /** - * Enum for specifying the text size. - *
      - *
    • SMALLEST is 50%
    • - *
    • SMALLER is 75%
    • - *
    • NORMAL is 100%
    • - *
    • LARGER is 150%
    • - *
    • LARGEST is 200%
    • - *
    - * - * @deprecated Use {@link WebSettings#setTextZoom(int)} and - * {@link WebSettings#getTextZoom()} instead. - */ - @Deprecated - public enum TextSize { - SMALLEST(50), SMALLER(75), NORMAL(100), LARGER(150), LARGEST(200); - - TextSize(int size) { - value = size; - } - - int value; - } - - /** - * Enum for specifying the WebView's desired density. - *
      - *
    • {@code FAR} makes 100% looking like in 240dpi
    • - *
    • {@code MEDIUM} makes 100% looking like in 160dpi
    • - *
    • {@code CLOSE} makes 100% looking like in 120dpi
    • - *
    - */ - public enum ZoomDensity { - FAR(150), // 240dpi - MEDIUM(100), // 160dpi - CLOSE(75); // 120dpi - - ZoomDensity(int size) { - value = size; - } - - /** - * @hide Only for use by WebViewProvider implementations - */ - public int getValue() { - return value; - } - - int value; - } - - public @interface CacheMode { - } - - /** - * Default cache usage mode. If the navigation type doesn't impose any specific - * behavior, use cached resources when they are available and not expired, - * otherwise load resources from the network. Use with {@link #setCacheMode}. - */ - public static final int LOAD_DEFAULT = -1; - /** - * Normal cache usage mode. Use with {@link #setCacheMode}. - * - * @deprecated This value is obsolete, as from API level - * {@link android.os.Build.VERSION_CODES#HONEYCOMB} and onwards it - * has the same effect as {@link #LOAD_DEFAULT}. - */ - @Deprecated - public static final int LOAD_NORMAL = 0; - /** - * Use cached resources when they are available, even if they have expired. - * Otherwise load resources from the network. Use with {@link #setCacheMode}. - */ - public static final int LOAD_CACHE_ELSE_NETWORK = 1; - /** - * Don't use the cache, load from the network. Use with {@link #setCacheMode}. - */ - public static final int LOAD_NO_CACHE = 2; - /** - * Don't use the network, load from the cache. Use with {@link #setCacheMode}. - */ - public static final int LOAD_CACHE_ONLY = 3; - - public enum RenderPriority { - NORMAL, HIGH, LOW - } - - /** - * The plugin state effects how plugins are treated on a page. ON means that any - * object will be loaded even if a plugin does not exist to handle the content. - * ON_DEMAND means that if there is a plugin installed that can handle the - * content, a placeholder is shown until the user clicks on the placeholder. - * Once clicked, the plugin will be enabled on the page. OFF means that all - * plugins will be turned off and any fallback content will be used. - */ - public enum PluginState { - ON, ON_DEMAND, OFF - } - - /** - * Used with {@link #setMixedContentMode} - * - * In this mode, the WebView will allow a secure origin to load content from any - * other origin, even if that origin is insecure. This is the least secure mode - * of operation for the WebView, and where possible apps should not set this - * mode. - */ - public static final int MIXED_CONTENT_ALWAYS_ALLOW = 0; - /** - * Used with {@link #setMixedContentMode} - * - * In this mode, the WebView will not allow a secure origin to load content from - * an insecure origin. This is the preferred and most secure mode of operation - * for the WebView and apps are strongly advised to use this mode. - */ - public static final int MIXED_CONTENT_NEVER_ALLOW = 1; - /** - * Used with {@link #setMixedContentMode} - * - * In this mode, the WebView will attempt to be compatible with the approach of - * a modern web browser with regard to mixed content. Some insecure content may - * be allowed to be loaded by a secure origin and other types of content will be - * blocked. The types of content are allowed or blocked may change release to - * release and are not explicitly defined. - * - * This mode is intended to be used by apps that are not in control of the - * content that they render but desire to operate in a reasonably secure - * environment. For highest security, apps are recommended to use - * {@link #MIXED_CONTENT_NEVER_ALLOW}. - */ - public static final int MIXED_CONTENT_COMPATIBILITY_MODE = 2; - - /** - * Enables dumping the pages navigation cache to a text file. The default is - * {@code false}. - * - * @deprecated This method is now obsolete. - * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} - */ - @Deprecated - public abstract void setNavDump(boolean enabled); - - /** - * Gets whether dumping the navigation cache is enabled. - * - * @return whether dumping the navigation cache is enabled - * @see #setNavDump - * @deprecated This method is now obsolete. - * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} - */ - @Deprecated - public abstract boolean getNavDump(); - - /** - * Sets whether the WebView should support zooming using its on-screen zoom - * controls and gestures. The particular zoom mechanisms that should be used can - * be set with {@link #setBuiltInZoomControls}. This setting does not affect - * zooming performed using the {@link WebView#zoomIn()} and - * {@link WebView#zoomOut()} methods. The default is {@code true}. - * - * @param support whether the WebView should support zoom - */ - public abstract void setSupportZoom(boolean support); - - /** - * Gets whether the WebView supports zoom. - * - * @return {@code true} if the WebView supports zoom - * @see #setSupportZoom - */ - public abstract boolean supportZoom(); - - /** - * Sets whether the WebView requires a user gesture to play media. The default - * is {@code true}. - * - * @param require whether the WebView requires a user gesture to play media - */ - public abstract void setMediaPlaybackRequiresUserGesture(boolean require); - - /** - * Gets whether the WebView requires a user gesture to play media. - * - * @return {@code true} if the WebView requires a user gesture to play media - * @see #setMediaPlaybackRequiresUserGesture - */ - public abstract boolean getMediaPlaybackRequiresUserGesture(); - - /** - * Sets whether the WebView should use its built-in zoom mechanisms. The - * built-in zoom mechanisms comprise on-screen zoom controls, which are - * displayed over the WebView's content, and the use of a pinch gesture to - * control zooming. Whether or not these on-screen controls are displayed can be - * set with {@link #setDisplayZoomControls}. The default is {@code false}. - *

    - * The built-in mechanisms are the only currently supported zoom mechanisms, so - * it is recommended that this setting is always enabled. - * - * @param enabled whether the WebView should use its built-in zoom mechanisms - */ - // This method was intended to select between the built-in zoom mechanisms - // and the separate zoom controls. The latter were obtained using - // {@link WebView#getZoomControls}, which is now hidden. - public abstract void setBuiltInZoomControls(boolean enabled); - - /** - * Gets whether the zoom mechanisms built into WebView are being used. - * - * @return {@code true} if the zoom mechanisms built into WebView are being used - * @see #setBuiltInZoomControls - */ - public abstract boolean getBuiltInZoomControls(); - - /** - * Sets whether the WebView should display on-screen zoom controls when using - * the built-in zoom mechanisms. See {@link #setBuiltInZoomControls}. The - * default is {@code true}. - * - * @param enabled whether the WebView should display on-screen zoom controls - */ - public abstract void setDisplayZoomControls(boolean enabled); - - /** - * Gets whether the WebView displays on-screen zoom controls when using the - * built-in zoom mechanisms. - * - * @return {@code true} if the WebView displays on-screen zoom controls when - * using the built-in zoom mechanisms - * @see #setDisplayZoomControls - */ - public abstract boolean getDisplayZoomControls(); - - /** - * Enables or disables file access within WebView. File access is enabled by - * default. Note that this enables or disables file system access only. Assets - * and resources are still accessible using file:///android_asset and - * file:///android_res. - */ - public abstract void setAllowFileAccess(boolean allow); - - /** - * Gets whether this WebView supports file access. - * - * @see #setAllowFileAccess - */ - public abstract boolean getAllowFileAccess(); - - /** - * Enables or disables content URL access within WebView. Content URL access - * allows WebView to load content from a content provider installed in the - * system. The default is enabled. - */ - public abstract void setAllowContentAccess(boolean allow); - - /** - * Gets whether this WebView supports content URL access. - * - * @see #setAllowContentAccess - */ - public abstract boolean getAllowContentAccess(); - - /** - * Sets whether the WebView loads pages in overview mode, that is, zooms out the - * content to fit on screen by width. This setting is taken into account when - * the content width is greater than the width of the WebView control, for - * example, when {@link #getUseWideViewPort} is enabled. The default is - * {@code false}. - */ - public abstract void setLoadWithOverviewMode(boolean overview); - - /** - * Gets whether this WebView loads pages in overview mode. - * - * @return whether this WebView loads pages in overview mode - * @see #setLoadWithOverviewMode - */ - public abstract boolean getLoadWithOverviewMode(); - - /** - * Sets whether the WebView will enable smooth transition while panning or - * zooming or while the window hosting the WebView does not have focus. If it is - * {@code true}, WebView will choose a solution to maximize the performance. - * e.g. the WebView's content may not be updated during the transition. If it is - * false, WebView will keep its fidelity. The default value is {@code false}. - * - * @deprecated This method is now obsolete, and will become a no-op in future. - */ - @Deprecated - public abstract void setEnableSmoothTransition(boolean enable); - - /** - * Gets whether the WebView enables smooth transition while panning or zooming. - * - * @see #setEnableSmoothTransition - * - * @deprecated This method is now obsolete, and will become a no-op in future. - */ - @Deprecated - public abstract boolean enableSmoothTransition(); - - /** - * Sets whether the WebView uses its background for over scroll background. If - * {@code true}, it will use the WebView's background. If {@code false}, it will - * use an internal pattern. Default is {@code true}. - * - * @deprecated This method is now obsolete. - * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} - */ - @Deprecated - public abstract void setUseWebViewBackgroundForOverscrollBackground(boolean view); - - /** - * Gets whether this WebView uses WebView's background instead of internal - * pattern for over scroll background. - * - * @see #setUseWebViewBackgroundForOverscrollBackground - * @deprecated This method is now obsolete. - * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} - */ - @Deprecated - public abstract boolean getUseWebViewBackgroundForOverscrollBackground(); - - /** - * Sets whether the WebView should save form data. In Android O, the platform - * has implemented a fully functional Autofill feature to store form data. - * Therefore, the Webview form data save feature is disabled. - * - * Note that the feature will continue to be supported on older versions of - * Android as before. - * - * This function does not have any effect. - */ - @Deprecated - public abstract void setSaveFormData(boolean save); - - /** - * Gets whether the WebView saves form data. - * - * @return whether the WebView saves form data - * @see #setSaveFormData - */ - @Deprecated - public abstract boolean getSaveFormData(); - - /** - * Sets whether the WebView should save passwords. The default is {@code true}. - * - * @deprecated Saving passwords in WebView will not be supported in future - * versions. - */ - @Deprecated - public abstract void setSavePassword(boolean save); - - /** - * Gets whether the WebView saves passwords. - * - * @return whether the WebView saves passwords - * @see #setSavePassword - * @deprecated Saving passwords in WebView will not be supported in future - * versions. - */ - @Deprecated - public abstract boolean getSavePassword(); - - /** - * Sets the text zoom of the page in percent. The default is 100. - * - * @param textZoom the text zoom in percent - */ - public abstract void setTextZoom(int textZoom); - - /** - * Gets the text zoom of the page in percent. - * - * @return the text zoom of the page in percent - * @see #setTextZoom - */ - public abstract int getTextZoom(); - - /** - * Sets policy for third party cookies. Developers should access this via - * {@link CookieManager#setShouldAcceptThirdPartyCookies}. - * - * @hide Internal API. - */ - public abstract void setAcceptThirdPartyCookies(boolean accept); - - /** - * Gets policy for third party cookies. Developers should access this via - * {@link CookieManager#getShouldAcceptThirdPartyCookies}. - * - * @hide Internal API - */ - public abstract boolean getAcceptThirdPartyCookies(); - - /** - * Sets the text size of the page. The default is {@link TextSize#NORMAL}. - * - * @param t the text size as a {@link TextSize} value - * @deprecated Use {@link #setTextZoom} instead. - */ - @Deprecated - public synchronized void setTextSize(TextSize t) { - setTextZoom(t.value); - } - - /** - * Gets the text size of the page. If the text size was previously specified in - * percent using {@link #setTextZoom}, this will return the closest matching - * {@link TextSize}. - * - * @return the text size as a {@link TextSize} value - * @see #setTextSize - * @deprecated Use {@link #getTextZoom} instead. - */ - @Deprecated - public synchronized TextSize getTextSize() { - return null; - } - - /** - * Sets the default zoom density of the page. This must be called from the UI - * thread. The default is {@link ZoomDensity#MEDIUM}. - * - * This setting is not recommended for use in new applications. If the WebView - * is utilized to display mobile-oriented pages, the desired effect can be - * achieved by adjusting 'width' and 'initial-scale' attributes of page's 'meta - * viewport' tag. For pages lacking the tag, - * {@link android.webkit.WebView#setInitialScale} and - * {@link #setUseWideViewPort} can be used. - * - * @param zoom the zoom density - * @deprecated This method is no longer supported, see the function - * documentation for recommended alternatives. - */ - @Deprecated - public abstract void setDefaultZoom(ZoomDensity zoom); - - /** - * Gets the default zoom density of the page. This should be called from the UI - * thread. - * - * This setting is not recommended for use in new applications. - * - * @return the zoom density - * @see #setDefaultZoom - * @deprecated Will only return the default value. - */ - @Deprecated - public abstract ZoomDensity getDefaultZoom(); - - /** - * Enables using light touches to make a selection and activate mouseovers. - * - * @deprecated From {@link android.os.Build.VERSION_CODES#JELLY_BEAN} this - * setting is obsolete and has no effect. - */ - @Deprecated - public abstract void setLightTouchEnabled(boolean enabled); - - /** - * Gets whether light touches are enabled. - * - * @see #setLightTouchEnabled - * @deprecated This setting is obsolete. - */ - @Deprecated - public abstract boolean getLightTouchEnabled(); - - /** - * Controlled a rendering optimization that is no longer present. Setting it now - * has no effect. - * - * @deprecated This setting now has no effect. - * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} - */ - @Deprecated - public void setUseDoubleTree(boolean use) { - // Specified to do nothing, so no need for derived classes to override. - } - - /** - * Controlled a rendering optimization that is no longer present. Setting it now - * has no effect. - * - * @deprecated This setting now has no effect. - * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} - */ - @Deprecated - public boolean getUseDoubleTree() { - // Returns false unconditionally, so no need for derived classes to override. - return false; - } - - /** - * Sets the user-agent string using an integer code. - *

      - *
    • 0 means the WebView should use an Android user-agent string
    • - *
    • 1 means the WebView should use a desktop user-agent string
    • - *
    - * Other values are ignored. The default is an Android user-agent string, i.e. - * code value 0. - * - * @param ua the integer code for the user-agent string - * @deprecated Please use {@link #setUserAgentString} instead. - * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} - */ - @Deprecated - public abstract void setUserAgent(int ua); - - /** - * Gets the user-agent as an integer code. - *
      - *
    • -1 means the WebView is using a custom user-agent string set with - * {@link #setUserAgentString}
    • - *
    • 0 means the WebView should use an Android user-agent string
    • - *
    • 1 means the WebView should use a desktop user-agent string
    • - *
    - * - * @return the integer code for the user-agent string - * @see #setUserAgent - * @deprecated Please use {@link #getUserAgentString} instead. - * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} - */ - @Deprecated - public abstract int getUserAgent(); - - /** - * Sets whether the WebView should enable support for the "viewport" - * HTML meta tag or should use a wide viewport. When the value of the setting is - * {@code false}, the layout width is always set to the width of the WebView - * control in device-independent (CSS) pixels. When the value is {@code true} - * and the page contains the viewport meta tag, the value of the width specified - * in the tag is used. If the page does not contain the tag or does not provide - * a width, then a wide viewport will be used. - * - * @param use whether to enable support for the viewport meta tag - */ - public abstract void setUseWideViewPort(boolean use); - - /** - * Gets whether the WebView supports the "viewport" HTML meta tag or - * will use a wide viewport. - * - * @return {@code true} if the WebView supports the viewport meta tag - * @see #setUseWideViewPort - */ - public abstract boolean getUseWideViewPort(); - - /** - * Sets whether the WebView whether supports multiple windows. If set to true, - * {@link WebChromeClient#onCreateWindow} must be implemented by the host - * application. The default is {@code false}. - * - * @param support whether to support multiple windows - */ - public abstract void setSupportMultipleWindows(boolean support); - - /** - * Gets whether the WebView supports multiple windows. - * - * @return {@code true} if the WebView supports multiple windows - * @see #setSupportMultipleWindows - */ - public abstract boolean supportMultipleWindows(); - - /** - * Sets the underlying layout algorithm. This will cause a re-layout of the - * WebView. The default is {@link LayoutAlgorithm#NARROW_COLUMNS}. - * - * @param l the layout algorithm to use, as a {@link LayoutAlgorithm} value - */ - public abstract void setLayoutAlgorithm(LayoutAlgorithm l); - - /** - * Gets the current layout algorithm. - * - * @return the layout algorithm in use, as a {@link LayoutAlgorithm} value - * @see #setLayoutAlgorithm - */ - public abstract LayoutAlgorithm getLayoutAlgorithm(); - - /** - * Sets the standard font family name. The default is "sans-serif". - * - * @param font a font family name - */ - public abstract void setStandardFontFamily(String font); - - /** - * Gets the standard font family name. - * - * @return the standard font family name as a string - * @see #setStandardFontFamily - */ - public abstract String getStandardFontFamily(); - - /** - * Sets the fixed font family name. The default is "monospace". - * - * @param font a font family name - */ - public abstract void setFixedFontFamily(String font); - - /** - * Gets the fixed font family name. - * - * @return the fixed font family name as a string - * @see #setFixedFontFamily - */ - public abstract String getFixedFontFamily(); - - /** - * Sets the sans-serif font family name. The default is "sans-serif". - * - * @param font a font family name - */ - public abstract void setSansSerifFontFamily(String font); - - /** - * Gets the sans-serif font family name. - * - * @return the sans-serif font family name as a string - * @see #setSansSerifFontFamily - */ - public abstract String getSansSerifFontFamily(); - - /** - * Sets the serif font family name. The default is "sans-serif". - * - * @param font a font family name - */ - public abstract void setSerifFontFamily(String font); - - /** - * Gets the serif font family name. The default is "serif". - * - * @return the serif font family name as a string - * @see #setSerifFontFamily - */ - public abstract String getSerifFontFamily(); - - /** - * Sets the cursive font family name. The default is "cursive". - * - * @param font a font family name - */ - public abstract void setCursiveFontFamily(String font); - - /** - * Gets the cursive font family name. - * - * @return the cursive font family name as a string - * @see #setCursiveFontFamily - */ +abstract public class WebSettings +{ + public WebSettings(){} + public WebSettings.TextSize getTextSize(){ return null; } public abstract String getCursiveFontFamily(); - - /** - * Sets the fantasy font family name. The default is "fantasy". - * - * @param font a font family name - */ - public abstract void setFantasyFontFamily(String font); - - /** - * Gets the fantasy font family name. - * - * @return the fantasy font family name as a string - * @see #setFantasyFontFamily - */ - public abstract String getFantasyFontFamily(); - - /** - * Sets the minimum font size. The default is 8. - * - * @param size a non-negative integer between 1 and 72. Any number outside the - * specified range will be pinned. - */ - public abstract void setMinimumFontSize(int size); - - /** - * Gets the minimum font size. - * - * @return a non-negative integer between 1 and 72 - * @see #setMinimumFontSize - */ - public abstract int getMinimumFontSize(); - - /** - * Sets the minimum logical font size. The default is 8. - * - * @param size a non-negative integer between 1 and 72. Any number outside the - * specified range will be pinned. - */ - public abstract void setMinimumLogicalFontSize(int size); - - /** - * Gets the minimum logical font size. - * - * @return a non-negative integer between 1 and 72 - * @see #setMinimumLogicalFontSize - */ - public abstract int getMinimumLogicalFontSize(); - - /** - * Sets the default font size. The default is 16. - * - * @param size a non-negative integer between 1 and 72. Any number outside the - * specified range will be pinned. - */ - public abstract void setDefaultFontSize(int size); - - /** - * Gets the default font size. - * - * @return a non-negative integer between 1 and 72 - * @see #setDefaultFontSize - */ - public abstract int getDefaultFontSize(); - - /** - * Sets the default fixed font size. The default is 16. - * - * @param size a non-negative integer between 1 and 72. Any number outside the - * specified range will be pinned. - */ - public abstract void setDefaultFixedFontSize(int size); - - /** - * Gets the default fixed font size. - * - * @return a non-negative integer between 1 and 72 - * @see #setDefaultFixedFontSize - */ - public abstract int getDefaultFixedFontSize(); - - /** - * Sets whether the WebView should load image resources. Note that this method - * controls loading of all images, including those embedded using the data URI - * scheme. Use {@link #setBlockNetworkImage} to control loading only of images - * specified using network URI schemes. Note that if the value of this setting - * is changed from {@code false} to {@code true}, all images resources - * referenced by content currently displayed by the WebView are loaded - * automatically. The default is {@code true}. - * - * @param flag whether the WebView should load image resources - */ - public abstract void setLoadsImagesAutomatically(boolean flag); - - /** - * Gets whether the WebView loads image resources. This includes images embedded - * using the data URI scheme. - * - * @return {@code true} if the WebView loads image resources - * @see #setLoadsImagesAutomatically - */ - public abstract boolean getLoadsImagesAutomatically(); - - /** - * Sets whether the WebView should not load image resources from the network - * (resources accessed via http and https URI schemes). Note that this method - * has no effect unless {@link #getLoadsImagesAutomatically} returns - * {@code true}. Also note that disabling all network loads using - * {@link #setBlockNetworkLoads} will also prevent network images from loading, - * even if this flag is set to false. When the value of this setting is changed - * from {@code true} to {@code false}, network images resources referenced by - * content currently displayed by the WebView are fetched automatically. The - * default is {@code false}. - * - * @param flag whether the WebView should not load image resources from the - * network - * @see #setBlockNetworkLoads - */ - public abstract void setBlockNetworkImage(boolean flag); - - /** - * Gets whether the WebView does not load image resources from the network. - * - * @return {@code true} if the WebView does not load image resources from the - * network - * @see #setBlockNetworkImage - */ - public abstract boolean getBlockNetworkImage(); - - /** - * Sets whether the WebView should not load resources from the network. Use - * {@link #setBlockNetworkImage} to only avoid loading image resources. Note - * that if the value of this setting is changed from {@code true} to - * {@code false}, network resources referenced by content currently displayed by - * the WebView are not fetched until {@link android.webkit.WebView#reload} is - * called. If the application does not have the - * {@link android.Manifest.permission#INTERNET} permission, attempts to set a - * value of {@code false} will cause a {@link java.lang.SecurityException} to be - * thrown. The default value is {@code false} if the application has the - * {@link android.Manifest.permission#INTERNET} permission, otherwise it is - * {@code true}. - * - * @param flag {@code true} means block network loads by the WebView - * @see android.webkit.WebView#reload - */ - public abstract void setBlockNetworkLoads(boolean flag); - - /** - * Gets whether the WebView does not load any resources from the network. - * - * @return {@code true} if the WebView does not load any resources from the - * network - * @see #setBlockNetworkLoads - */ - public abstract boolean getBlockNetworkLoads(); - - /** - * Tells the WebView to enable JavaScript execution. The default is - * {@code false}. - * - * @param flag {@code true} if the WebView should execute JavaScript - */ - public abstract void setJavaScriptEnabled(boolean flag); - - /** - * Sets whether JavaScript running in the context of a file scheme URL should be - * allowed to access content from any origin. This includes access to content - * from other file scheme URLs. See {@link #setAllowFileAccessFromFileURLs}. To - * enable the most restrictive, and therefore secure policy, this setting should - * be disabled. Note that this setting affects only JavaScript access to file - * scheme resources. Other access to such resources, for example, from image - * HTML elements, is unaffected. To prevent possible violation of same domain - * policy when targeting - * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and earlier, - * you should explicitly set this value to {@code false}. - *

    - * The default value is {@code true} for apps targeting - * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and below, and - * {@code false} when targeting - * {@link android.os.Build.VERSION_CODES#JELLY_BEAN} and above. - * - * @param flag whether JavaScript running in the context of a file scheme URL - * should be allowed to access content from any origin - */ - public abstract void setAllowUniversalAccessFromFileURLs(boolean flag); - - /** - * Sets whether JavaScript running in the context of a file scheme URL should be - * allowed to access content from other file scheme URLs. To enable the most - * restrictive, and therefore secure, policy this setting should be disabled. - * Note that the value of this setting is ignored if the value of - * {@link #getAllowUniversalAccessFromFileURLs} is {@code true}. Note too, that - * this setting affects only JavaScript access to file scheme resources. Other - * access to such resources, for example, from image HTML elements, is - * unaffected. To prevent possible violation of same domain policy when - * targeting {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and - * earlier, you should explicitly set this value to {@code false}. - *

    - * The default value is {@code true} for apps targeting - * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and below, and - * {@code false} when targeting - * {@link android.os.Build.VERSION_CODES#JELLY_BEAN} and above. - * - * @param flag whether JavaScript running in the context of a file scheme URL - * should be allowed to access content from other file scheme URLs - */ - public abstract void setAllowFileAccessFromFileURLs(boolean flag); - - /** - * Sets whether the WebView should enable plugins. The default is {@code false}. - * - * @param flag {@code true} if plugins should be enabled - * @deprecated This method has been deprecated in favor of - * {@link #setPluginState} - * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2} - */ - @Deprecated - public abstract void setPluginsEnabled(boolean flag); - - /** - * Tells the WebView to enable, disable, or have plugins on demand. On demand - * mode means that if a plugin exists that can handle the embedded content, a - * placeholder icon will be shown instead of the plugin. When the placeholder is - * clicked, the plugin will be enabled. The default is {@link PluginState#OFF}. - * - * @param state a PluginState value - * @deprecated Plugins will not be supported in future, and should not be used. - */ - @Deprecated - public abstract void setPluginState(PluginState state); - - /** - * Sets a custom path to plugins used by the WebView. This method is obsolete - * since each plugin is now loaded from its own package. - * - * @param pluginsPath a String path to the directory containing plugins - * @deprecated This method is no longer used as plugins are loaded from their - * own APK via the system's package manager. - * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2} - */ - @Deprecated - public void setPluginsPath(String pluginsPath) { - // Specified to do nothing, so no need for derived classes to override. - } - - /** - * Sets the path to where database storage API databases should be saved. In - * order for the database storage API to function correctly, this method must be - * called with a path to which the application can write. This method should - * only be called once: repeated calls are ignored. - * - * @param databasePath a path to the directory where databases should be saved. - * @deprecated Database paths are managed by the implementation and calling this - * method will have no effect. - */ - @Deprecated - public abstract void setDatabasePath(String databasePath); - - /** - * Sets the path where the Geolocation databases should be saved. In order for - * Geolocation permissions and cached positions to be persisted, this method - * must be called with a path to which the application can write. - * - * @param databasePath a path to the directory where databases should be saved. - * @deprecated Geolocation database are managed by the implementation and - * calling this method will have no effect. - */ - @Deprecated - public abstract void setGeolocationDatabasePath(String databasePath); - - /** - * Sets whether the Application Caches API should be enabled. The default is - * {@code false}. Note that in order for the Application Caches API to be - * enabled, a valid database path must also be supplied to - * {@link #setAppCachePath}. - * - * @param flag {@code true} if the WebView should enable Application Caches - */ - public abstract void setAppCacheEnabled(boolean flag); - - /** - * Sets the path to the Application Caches files. In order for the Application - * Caches API to be enabled, this method must be called with a path to which the - * application can write. This method should only be called once: repeated calls - * are ignored. - * - * @param appCachePath a String path to the directory containing Application - * Caches files. - * @see #setAppCacheEnabled - */ - public abstract void setAppCachePath(String appCachePath); - - /** - * Sets the maximum size for the Application Cache content. The passed size will - * be rounded to the nearest value that the database can support, so this should - * be viewed as a guide, not a hard limit. Setting the size to a value less than - * current database size does not cause the database to be trimmed. The default - * size is {@link Long#MAX_VALUE}. It is recommended to leave the maximum size - * set to the default value. - * - * @param appCacheMaxSize the maximum size in bytes - * @deprecated In future quota will be managed automatically. - */ - @Deprecated - public abstract void setAppCacheMaxSize(long appCacheMaxSize); - - /** - * Sets whether the database storage API is enabled. The default value is false. - * See also {@link #setDatabasePath} for how to correctly set up the database - * storage API. - * - * This setting is global in effect, across all WebView instances in a process. - * Note you should only modify this setting prior to making any WebView - * page load within a given process, as the WebView implementation may ignore - * changes to this setting after that point. - * - * @param flag {@code true} if the WebView should use the database storage API - */ - public abstract void setDatabaseEnabled(boolean flag); - - /** - * Sets whether the DOM storage API is enabled. The default value is - * {@code false}. - * - * @param flag {@code true} if the WebView should use the DOM storage API - */ - public abstract void setDomStorageEnabled(boolean flag); - - /** - * Gets whether the DOM Storage APIs are enabled. - * - * @return {@code true} if the DOM Storage APIs are enabled - * @see #setDomStorageEnabled - */ - public abstract boolean getDomStorageEnabled(); - - /** - * Gets the path to where database storage API databases are saved. - * - * @return the String path to the database storage API databases - * @see #setDatabasePath - * @deprecated Database paths are managed by the implementation this method is - * obsolete. - */ - @Deprecated public abstract String getDatabasePath(); - - /** - * Gets whether the database storage API is enabled. - * - * @return {@code true} if the database storage API is enabled - * @see #setDatabaseEnabled - */ - public abstract boolean getDatabaseEnabled(); - - /** - * Sets whether Geolocation is enabled. The default is {@code true}. - *

    - * Please note that in order for the Geolocation API to be usable by a page in - * the WebView, the following requirements must be met: - *

      - *
    • an application must have permission to access the device location, see - * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}, - * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}; - *
    • an application must provide an implementation of the - * {@link WebChromeClient#onGeolocationPermissionsShowPrompt} callback to - * receive notifications that a page is requesting access to location via the - * JavaScript Geolocation API. - *
    - *

    - * - * @param flag whether Geolocation should be enabled - */ - public abstract void setGeolocationEnabled(boolean flag); - - /** - * Gets whether JavaScript is enabled. - * - * @return {@code true} if JavaScript is enabled - * @see #setJavaScriptEnabled - */ - public abstract boolean getJavaScriptEnabled(); - - /** - * Gets whether JavaScript running in the context of a file scheme URL can - * access content from any origin. This includes access to content from other - * file scheme URLs. - * - * @return whether JavaScript running in the context of a file scheme URL can - * access content from any origin - * @see #setAllowUniversalAccessFromFileURLs - */ - public abstract boolean getAllowUniversalAccessFromFileURLs(); - - /** - * Gets whether JavaScript running in the context of a file scheme URL can - * access content from other file scheme URLs. - * - * @return whether JavaScript running in the context of a file scheme URL can - * access content from other file scheme URLs - * @see #setAllowFileAccessFromFileURLs - */ - public abstract boolean getAllowFileAccessFromFileURLs(); - - /** - * Gets whether plugins are enabled. - * - * @return {@code true} if plugins are enabled - * @see #setPluginsEnabled - * @deprecated This method has been replaced by {@link #getPluginState} - * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2} - */ - @Deprecated - public abstract boolean getPluginsEnabled(); - - /** - * Gets the current state regarding whether plugins are enabled. - * - * @return the plugin state as a {@link PluginState} value - * @see #setPluginState - * @deprecated Plugins will not be supported in future, and should not be used. - */ - @Deprecated - public abstract PluginState getPluginState(); - - /** - * Gets the directory that contains the plugin libraries. This method is - * obsolete since each plugin is now loaded from its own package. - * - * @return an empty string - * @deprecated This method is no longer used as plugins are loaded from their - * own APK via the system's package manager. - * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2} - */ - @Deprecated - public String getPluginsPath() { - // Unconditionally returns empty string, so no need for derived classes to - // override. - return ""; - } - - /** - * Tells JavaScript to open windows automatically. This applies to the - * JavaScript function {@code window.open()}. The default is {@code false}. - * - * @param flag {@code true} if JavaScript can open windows automatically - */ - public abstract void setJavaScriptCanOpenWindowsAutomatically(boolean flag); - - /** - * Gets whether JavaScript can open windows automatically. - * - * @return {@code true} if JavaScript can open windows automatically during - * {@code window.open()} - * @see #setJavaScriptCanOpenWindowsAutomatically - */ - public abstract boolean getJavaScriptCanOpenWindowsAutomatically(); - - /** - * Sets the default text encoding name to use when decoding html pages. The - * default is "UTF-8". - * - * @param encoding the text encoding name - */ - public abstract void setDefaultTextEncodingName(String encoding); - - /** - * Gets the default text encoding name. - * - * @return the default text encoding name as a string - * @see #setDefaultTextEncodingName - */ public abstract String getDefaultTextEncodingName(); - - /** - * Sets the WebView's user-agent string. If the string is {@code null} or empty, - * the system default value will be used. - * - * Note that starting from {@link android.os.Build.VERSION_CODES#KITKAT} Android - * version, changing the user-agent while loading a web page causes WebView to - * initiate loading once again. - * - * @param ua new user-agent string - */ - public abstract void setUserAgentString(String ua); - - /** - * Gets the WebView's user-agent string. - * - * @return the WebView's user-agent string - * @see #setUserAgentString - */ + public abstract String getFantasyFontFamily(); + public abstract String getFixedFontFamily(); + public abstract String getSansSerifFontFamily(); + public abstract String getSerifFontFamily(); + public abstract String getStandardFontFamily(); public abstract String getUserAgentString(); - - /** - * Returns the default User-Agent used by a WebView. An instance of WebView - * could use a different User-Agent if a call is made to - * {@link WebSettings#setUserAgentString(String)}. - * - * @param context a Context object used to access application assets - */ - public static String getDefaultUserAgent(Context context) { - return null; - } - - /** - * Tells the WebView whether it needs to set a node to have focus when - * {@link WebView#requestFocus(int, android.graphics.Rect)} is called. The - * default value is {@code true}. - * - * @param flag whether the WebView needs to set a node - */ - public abstract void setNeedInitialFocus(boolean flag); - - /** - * Sets the priority of the Render thread. Unlike the other settings, this one - * only needs to be called once per process. The default value is - * {@link RenderPriority#NORMAL}. - * - * @param priority the priority - * @deprecated It is not recommended to adjust thread priorities, and this will - * not be supported in future versions. - */ - @Deprecated - public abstract void setRenderPriority(RenderPriority priority); - - /** - * Overrides the way the cache is used. The way the cache is used is based on - * the navigation type. For a normal page load, the cache is checked and content - * is re-validated as needed. When navigating back, content is not revalidated, - * instead the content is just retrieved from the cache. This method allows the - * client to override this behavior by specifying one of {@link #LOAD_DEFAULT}, - * {@link #LOAD_CACHE_ELSE_NETWORK}, {@link #LOAD_NO_CACHE} or - * {@link #LOAD_CACHE_ONLY}. The default value is {@link #LOAD_DEFAULT}. - * - * @param mode the mode to use - */ - public abstract void setCacheMode(@CacheMode int mode); - - /** - * Gets the current setting for overriding the cache mode. - * - * @return the current setting for overriding the cache mode - * @see #setCacheMode - */ - public abstract int getCacheMode(); - - /** - * Configures the WebView's behavior when a secure origin attempts to load a - * resource from an insecure origin. - * - * By default, apps that target {@link android.os.Build.VERSION_CODES#KITKAT} or - * below default to {@link #MIXED_CONTENT_ALWAYS_ALLOW}. Apps targeting - * {@link android.os.Build.VERSION_CODES#LOLLIPOP} default to - * {@link #MIXED_CONTENT_NEVER_ALLOW}. - * - * The preferred and most secure mode of operation for the WebView is - * {@link #MIXED_CONTENT_NEVER_ALLOW} and use of - * {@link #MIXED_CONTENT_ALWAYS_ALLOW} is strongly discouraged. - * - * @param mode The mixed content mode to use. One of - * {@link #MIXED_CONTENT_NEVER_ALLOW}, - * {@link #MIXED_CONTENT_ALWAYS_ALLOW} or - * {@link #MIXED_CONTENT_COMPATIBILITY_MODE}. - */ - public abstract void setMixedContentMode(int mode); - - /** - * Gets the current behavior of the WebView with regard to loading insecure - * content from a secure origin. - * - * @return The current setting, one of {@link #MIXED_CONTENT_NEVER_ALLOW}, - * {@link #MIXED_CONTENT_ALWAYS_ALLOW} or - * {@link #MIXED_CONTENT_COMPATIBILITY_MODE}. - */ - public abstract int getMixedContentMode(); - - /** - * Sets whether to use a video overlay for embedded encrypted video. In API - * levels prior to {@link android.os.Build.VERSION_CODES#LOLLIPOP}, encrypted - * video can only be rendered directly on a secure video surface, so it had been - * a hard problem to play encrypted video in HTML. When this flag is on, WebView - * can play encrypted video (MSE/EME) by using a video overlay (aka - * hole-punching) for videos embedded using HTML <video> tag.
    - * Caution: This setting is intended for use only in a narrow set of - * circumstances and apps should only enable it if they require playback of - * encrypted video content. It will impose the following limitations on the - * WebView: - *

      - *
    • Only one video overlay can be played at a time. - *
    • Changes made to position or dimensions of a video element may be - * propagated to the corresponding video overlay with a noticeable delay. - *
    • The video overlay is not visible to web APIs and as such may not interact - * with script or styling. For example, CSS styles applied to the <video> - * tag may be ignored. - *
    - * This is not an exhaustive set of constraints and it may vary with new - * versions of the WebView. - * - * @hide - */ - public abstract void setVideoOverlayForEmbeddedEncryptedVideoEnabled(boolean flag); - - /** - * Gets whether a video overlay will be used for embedded encrypted video. - * - * @return {@code true} if WebView uses a video overlay for embedded encrypted - * video. - * @see #setVideoOverlayForEmbeddedEncryptedVideoEnabled - * @hide - */ - public abstract boolean getVideoOverlayForEmbeddedEncryptedVideoEnabled(); - - /** - * Sets whether this WebView should raster tiles when it is offscreen but - * attached to a window. Turning this on can avoid rendering artifacts when - * animating an offscreen WebView on-screen. Offscreen WebViews in this mode use - * more memory. The default value is false.
    - * Please follow these guidelines to limit memory usage: - *
      - *
    • WebView size should be not be larger than the device screen size. - *
    • Limit use of this mode to a small number of WebViews. Use it for visible - * WebViews and WebViews about to be animated to visible. - *
    - */ - public abstract void setOffscreenPreRaster(boolean enabled); - - /** - * Gets whether this WebView should raster tiles when it is offscreen but - * attached to a window. - * - * @return {@code true} if this WebView will raster tiles when it is offscreen - * but attached to a window. - */ + public abstract WebSettings.LayoutAlgorithm getLayoutAlgorithm(); + public abstract WebSettings.PluginState getPluginState(); + public abstract WebSettings.ZoomDensity getDefaultZoom(); + public abstract boolean enableSmoothTransition(); + public abstract boolean getAllowContentAccess(); + public abstract boolean getAllowFileAccess(); + public abstract boolean getAllowFileAccessFromFileURLs(); + public abstract boolean getAllowUniversalAccessFromFileURLs(); + public abstract boolean getBlockNetworkImage(); + public abstract boolean getBlockNetworkLoads(); + public abstract boolean getBuiltInZoomControls(); + public abstract boolean getDatabaseEnabled(); + public abstract boolean getDisplayZoomControls(); + public abstract boolean getDomStorageEnabled(); + public abstract boolean getJavaScriptCanOpenWindowsAutomatically(); + public abstract boolean getJavaScriptEnabled(); + public abstract boolean getLightTouchEnabled(); + public abstract boolean getLoadWithOverviewMode(); + public abstract boolean getLoadsImagesAutomatically(); + public abstract boolean getMediaPlaybackRequiresUserGesture(); public abstract boolean getOffscreenPreRaster(); - - /** - * Sets whether Safe Browsing is enabled. Safe Browsing allows WebView to - * protect against malware and phishing attacks by verifying the links. - * - *

    - * Safe Browsing can be disabled for all WebViews using a manifest tag (read - * general Safe - * Browsing info). The manifest tag has a lower precedence than this API. - * - *

    - * Safe Browsing is enabled by default for devices which support it. - * - * @param enabled Whether Safe Browsing is enabled. - */ - public abstract void setSafeBrowsingEnabled(boolean enabled); - - /** - * Gets whether Safe Browsing is enabled. See {@link #setSafeBrowsingEnabled}. - * - * @return {@code true} if Safe Browsing is enabled and {@code false} otherwise. - */ public abstract boolean getSafeBrowsingEnabled(); + public abstract boolean getSaveFormData(); + public abstract boolean getSavePassword(); + public abstract boolean getUseWideViewPort(); + public abstract boolean supportMultipleWindows(); + public abstract boolean supportZoom(); + public abstract int getCacheMode(); + public abstract int getDefaultFixedFontSize(); + public abstract int getDefaultFontSize(); + public abstract int getDisabledActionModeMenuItems(); + public abstract int getMinimumFontSize(); + public abstract int getMinimumLogicalFontSize(); + public abstract int getMixedContentMode(); + public abstract int getTextZoom(); + public abstract void setAllowContentAccess(boolean p0); + public abstract void setAllowFileAccess(boolean p0); + public abstract void setAllowFileAccessFromFileURLs(boolean p0); + public abstract void setAllowUniversalAccessFromFileURLs(boolean p0); + public abstract void setAppCacheEnabled(boolean p0); + public abstract void setAppCacheMaxSize(long p0); + public abstract void setAppCachePath(String p0); + public abstract void setBlockNetworkImage(boolean p0); + public abstract void setBlockNetworkLoads(boolean p0); + public abstract void setBuiltInZoomControls(boolean p0); + public abstract void setCacheMode(int p0); + public abstract void setCursiveFontFamily(String p0); + public abstract void setDatabaseEnabled(boolean p0); + public abstract void setDatabasePath(String p0); + public abstract void setDefaultFixedFontSize(int p0); + public abstract void setDefaultFontSize(int p0); + public abstract void setDefaultTextEncodingName(String p0); + public abstract void setDefaultZoom(WebSettings.ZoomDensity p0); + public abstract void setDisabledActionModeMenuItems(int p0); + public abstract void setDisplayZoomControls(boolean p0); + public abstract void setDomStorageEnabled(boolean p0); + public abstract void setEnableSmoothTransition(boolean p0); + public abstract void setFantasyFontFamily(String p0); + public abstract void setFixedFontFamily(String p0); + public abstract void setGeolocationDatabasePath(String p0); + public abstract void setGeolocationEnabled(boolean p0); + public abstract void setJavaScriptCanOpenWindowsAutomatically(boolean p0); + public abstract void setJavaScriptEnabled(boolean p0); + public abstract void setLayoutAlgorithm(WebSettings.LayoutAlgorithm p0); + public abstract void setLightTouchEnabled(boolean p0); + public abstract void setLoadWithOverviewMode(boolean p0); + public abstract void setLoadsImagesAutomatically(boolean p0); + public abstract void setMediaPlaybackRequiresUserGesture(boolean p0); + public abstract void setMinimumFontSize(int p0); + public abstract void setMinimumLogicalFontSize(int p0); + public abstract void setMixedContentMode(int p0); + public abstract void setNeedInitialFocus(boolean p0); + public abstract void setOffscreenPreRaster(boolean p0); + public abstract void setPluginState(WebSettings.PluginState p0); + public abstract void setRenderPriority(WebSettings.RenderPriority p0); + public abstract void setSafeBrowsingEnabled(boolean p0); + public abstract void setSansSerifFontFamily(String p0); + public abstract void setSaveFormData(boolean p0); + public abstract void setSavePassword(boolean p0); + public abstract void setSerifFontFamily(String p0); + public abstract void setStandardFontFamily(String p0); + public abstract void setSupportMultipleWindows(boolean p0); + public abstract void setSupportZoom(boolean p0); + public abstract void setTextZoom(int p0); + public abstract void setUseWideViewPort(boolean p0); + public abstract void setUserAgentString(String p0); + public int getForceDark(){ return 0; } + public static String getDefaultUserAgent(Context p0){ return null; } + public static int FORCE_DARK_AUTO = 0; + public static int FORCE_DARK_OFF = 0; + public static int FORCE_DARK_ON = 0; + public static int LOAD_CACHE_ELSE_NETWORK = 0; + public static int LOAD_CACHE_ONLY = 0; + public static int LOAD_DEFAULT = 0; + public static int LOAD_NORMAL = 0; + public static int LOAD_NO_CACHE = 0; + public static int MENU_ITEM_NONE = 0; + public static int MENU_ITEM_PROCESS_TEXT = 0; + public static int MENU_ITEM_SHARE = 0; + public static int MENU_ITEM_WEB_SEARCH = 0; + public static int MIXED_CONTENT_ALWAYS_ALLOW = 0; + public static int MIXED_CONTENT_COMPATIBILITY_MODE = 0; + public static int MIXED_CONTENT_NEVER_ALLOW = 0; + public void setForceDark(int p0){} + public void setTextSize(WebSettings.TextSize p0){} + static public enum LayoutAlgorithm + { + NARROW_COLUMNS, NORMAL, SINGLE_COLUMN, TEXT_AUTOSIZING; + private LayoutAlgorithm() {} + } + static public enum PluginState + { + OFF, ON, ON_DEMAND; + private PluginState() {} + } + static public enum RenderPriority + { + HIGH, LOW, NORMAL; + private RenderPriority() {} + } + static public enum TextSize + { + LARGER, LARGEST, NORMAL, SMALLER, SMALLEST; + private TextSize() {} + } + static public enum ZoomDensity + { + CLOSE, FAR, MEDIUM; + private ZoomDensity() {} + } } diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebStorage.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebStorage.java new file mode 100644 index 00000000000..c63a282b454 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebStorage.java @@ -0,0 +1,21 @@ +// Generated automatically from android.webkit.WebStorage for testing purposes + +package android.webkit; + +import android.webkit.ValueCallback; +import java.util.Map; + +public class WebStorage +{ + public static WebStorage getInstance(){ return null; } + public void deleteAllData(){} + public void deleteOrigin(String p0){} + public void getOrigins(ValueCallback p0){} + public void getQuotaForOrigin(String p0, ValueCallback p1){} + public void getUsageForOrigin(String p0, ValueCallback p1){} + public void setQuotaForOrigin(String p0, long p1){} + static public interface QuotaUpdater + { + void updateQuota(long p0); + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebView.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebView.java index 3065a9df966..b654cc05f61 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebView.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebView.java @@ -1,151 +1,259 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ +// Generated automatically from android.webkit.WebView for testing purposes package android.webkit; import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.res.Configuration; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Picture; +import android.graphics.Rect; +import android.net.Uri; +import android.net.http.SslCertificate; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.print.PrintDocumentAdapter; +import android.util.AttributeSet; +import android.util.LongSparseArray; +import android.util.SparseArray; +import android.view.DragEvent; +import android.view.KeyEvent; +import android.view.MotionEvent; import android.view.View; +import android.view.ViewGroup; +import android.view.ViewStructure; +import android.view.ViewTreeObserver; +import android.view.WindowInsets; +import android.view.accessibility.AccessibilityNodeProvider; +import android.view.autofill.AutofillId; +import android.view.autofill.AutofillValue; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputConnection; +import android.view.textclassifier.TextClassifier; +import android.view.translation.TranslationCapability; +import android.view.translation.ViewTranslationRequest; +import android.view.translation.ViewTranslationResponse; +import android.webkit.DownloadListener; +import android.webkit.ValueCallback; +import android.webkit.WebBackForwardList; +import android.webkit.WebChromeClient; +import android.webkit.WebMessage; +import android.webkit.WebMessagePort; +import android.webkit.WebSettings; +import android.webkit.WebViewClient; +import android.webkit.WebViewRenderProcess; +import android.webkit.WebViewRenderProcessClient; +import android.widget.AbsoluteLayout; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executor; +import java.util.function.Consumer; -public class WebView extends View { - - public WebView(Context context) { - super(context); +public class WebView extends AbsoluteLayout implements ViewGroup.OnHierarchyChangeListener, ViewTreeObserver.OnGlobalFocusChangeListener +{ + protected WebView() {} + abstract static public class VisualStateCallback + { + public VisualStateCallback(){} + public abstract void onComplete(long p0); } - - public void setHorizontalScrollbarOverlay(boolean overlay) {} - - public void setVerticalScrollbarOverlay(boolean overlay) {} - - public boolean overlayHorizontalScrollbar() { - return false; + protected int computeHorizontalScrollOffset(){ return 0; } + protected int computeHorizontalScrollRange(){ return 0; } + protected int computeVerticalScrollExtent(){ return 0; } + protected int computeVerticalScrollOffset(){ return 0; } + protected int computeVerticalScrollRange(){ return 0; } + protected void dispatchDraw(Canvas p0){} + protected void onAttachedToWindow(){} + protected void onConfigurationChanged(Configuration p0){} + protected void onDraw(Canvas p0){} + protected void onFocusChanged(boolean p0, int p1, Rect p2){} + protected void onMeasure(int p0, int p1){} + protected void onOverScrolled(int p0, int p1, boolean p2, boolean p3){} + protected void onScrollChanged(int p0, int p1, int p2, int p3){} + protected void onSizeChanged(int p0, int p1, int p2, int p3){} + protected void onVisibilityChanged(View p0, int p1){} + protected void onWindowVisibilityChanged(int p0){} + public AccessibilityNodeProvider getAccessibilityNodeProvider(){ return null; } + public Bitmap getFavicon(){ return null; } + public CharSequence getAccessibilityClassName(){ return null; } + public Handler getHandler(){ return null; } + public InputConnection onCreateInputConnection(EditorInfo p0){ return null; } + public Looper getWebViewLooper(){ return null; } + public Picture capturePicture(){ return null; } + public PrintDocumentAdapter createPrintDocumentAdapter(){ return null; } + public PrintDocumentAdapter createPrintDocumentAdapter(String p0){ return null; } + public SslCertificate getCertificate(){ return null; } + public String getOriginalUrl(){ return null; } + public String getTitle(){ return null; } + public String getUrl(){ return null; } + public String[] getHttpAuthUsernamePassword(String p0, String p1){ return null; } + public TextClassifier getTextClassifier(){ return null; } + public View findFocus(){ return null; } + public WebBackForwardList copyBackForwardList(){ return null; } + public WebBackForwardList restoreState(Bundle p0){ return null; } + public WebBackForwardList saveState(Bundle p0){ return null; } + public WebChromeClient getWebChromeClient(){ return null; } + public WebMessagePort[] createWebMessageChannel(){ return null; } + public WebSettings getSettings(){ return null; } + public WebView(Context p0){} + public WebView(Context p0, AttributeSet p1){} + public WebView(Context p0, AttributeSet p1, int p2){} + public WebView(Context p0, AttributeSet p1, int p2, boolean p3){} + public WebView(Context p0, AttributeSet p1, int p2, int p3){} + public WebView.HitTestResult getHitTestResult(){ return null; } + public WebViewClient getWebViewClient(){ return null; } + public WebViewRenderProcess getWebViewRenderProcess(){ return null; } + public WebViewRenderProcessClient getWebViewRenderProcessClient(){ return null; } + public WindowInsets onApplyWindowInsets(WindowInsets p0){ return null; } + public boolean canGoBack(){ return false; } + public boolean canGoBackOrForward(int p0){ return false; } + public boolean canGoForward(){ return false; } + public boolean canZoomIn(){ return false; } + public boolean canZoomOut(){ return false; } + public boolean dispatchKeyEvent(KeyEvent p0){ return false; } + public boolean getRendererPriorityWaivedWhenNotVisible(){ return false; } + public boolean isPrivateBrowsingEnabled(){ return false; } + public boolean isVisibleToUserForAutofill(int p0){ return false; } + public boolean onCheckIsTextEditor(){ return false; } + public boolean onDragEvent(DragEvent p0){ return false; } + public boolean onGenericMotionEvent(MotionEvent p0){ return false; } + public boolean onHoverEvent(MotionEvent p0){ return false; } + public boolean onKeyDown(int p0, KeyEvent p1){ return false; } + public boolean onKeyMultiple(int p0, int p1, KeyEvent p2){ return false; } + public boolean onKeyUp(int p0, KeyEvent p1){ return false; } + public boolean onTouchEvent(MotionEvent p0){ return false; } + public boolean onTrackballEvent(MotionEvent p0){ return false; } + public boolean overlayHorizontalScrollbar(){ return false; } + public boolean overlayVerticalScrollbar(){ return false; } + public boolean pageDown(boolean p0){ return false; } + public boolean pageUp(boolean p0){ return false; } + public boolean performLongClick(){ return false; } + public boolean requestChildRectangleOnScreen(View p0, Rect p1, boolean p2){ return false; } + public boolean requestFocus(int p0, Rect p1){ return false; } + public boolean shouldDelayChildPressedState(){ return false; } + public boolean showFindDialog(String p0, boolean p1){ return false; } + public boolean zoomIn(){ return false; } + public boolean zoomOut(){ return false; } + public float getScale(){ return 0; } + public int findAll(String p0){ return 0; } + public int getContentHeight(){ return 0; } + public int getProgress(){ return 0; } + public int getRendererRequestedPriority(){ return 0; } + public static ClassLoader getWebViewClassLoader(){ return null; } + public static PackageInfo getCurrentWebViewPackage(){ return null; } + public static String SCHEME_GEO = null; + public static String SCHEME_MAILTO = null; + public static String SCHEME_TEL = null; + public static String findAddress(String p0){ return null; } + public static Uri getSafeBrowsingPrivacyPolicyUrl(){ return null; } + public static int RENDERER_PRIORITY_BOUND = 0; + public static int RENDERER_PRIORITY_IMPORTANT = 0; + public static int RENDERER_PRIORITY_WAIVED = 0; + public static void clearClientCertPreferences(Runnable p0){} + public static void disableWebView(){} + public static void enableSlowWholeDocumentDraw(){} + public static void setDataDirectorySuffix(String p0){} + public static void setSafeBrowsingWhitelist(List p0, ValueCallback p1){} + public static void setWebContentsDebuggingEnabled(boolean p0){} + public static void startSafeBrowsing(Context p0, ValueCallback p1){} + public void addJavascriptInterface(Object p0, String p1){} + public void autofill(SparseArray p0){} + public void clearCache(boolean p0){} + public void clearFormData(){} + public void clearHistory(){} + public void clearMatches(){} + public void clearSslPreferences(){} + public void clearView(){} + public void computeScroll(){} + public void destroy(){} + public void dispatchCreateViewTranslationRequest(Map p0, int[] p1, TranslationCapability p2, List p3){} + public void documentHasImages(Message p0){} + public void evaluateJavascript(String p0, ValueCallback p1){} + public void findAllAsync(String p0){} + public void findNext(boolean p0){} + public void flingScroll(int p0, int p1){} + public void freeMemory(){} + public void goBack(){} + public void goBackOrForward(int p0){} + public void goForward(){} + public void invokeZoomPicker(){} + public void loadData(String p0, String p1, String p2){} + public void loadDataWithBaseURL(String p0, String p1, String p2, String p3, String p4){} + public void loadUrl(String p0){} + public void loadUrl(String p0, Map p1){} + public void onChildViewAdded(View p0, View p1){} + public void onChildViewRemoved(View p0, View p1){} + public void onCreateVirtualViewTranslationRequests(long[] p0, int[] p1, Consumer p2){} + public void onFinishTemporaryDetach(){} + public void onGlobalFocusChanged(View p0, View p1){} + public void onPause(){} + public void onProvideAutofillVirtualStructure(ViewStructure p0, int p1){} + public void onProvideContentCaptureStructure(ViewStructure p0, int p1){} + public void onProvideVirtualStructure(ViewStructure p0){} + public void onResume(){} + public void onStartTemporaryDetach(){} + public void onVirtualViewTranslationResponses(LongSparseArray p0){} + public void onWindowFocusChanged(boolean p0){} + public void pauseTimers(){} + public void postUrl(String p0, byte[] p1){} + public void postVisualStateCallback(long p0, WebView.VisualStateCallback p1){} + public void postWebMessage(WebMessage p0, Uri p1){} + public void reload(){} + public void removeJavascriptInterface(String p0){} + public void requestFocusNodeHref(Message p0){} + public void requestImageRef(Message p0){} + public void resumeTimers(){} + public void savePassword(String p0, String p1, String p2){} + public void saveWebArchive(String p0){} + public void saveWebArchive(String p0, boolean p1, ValueCallback p2){} + public void setBackgroundColor(int p0){} + public void setCertificate(SslCertificate p0){} + public void setDownloadListener(DownloadListener p0){} + public void setFindListener(WebView.FindListener p0){} + public void setHorizontalScrollbarOverlay(boolean p0){} + public void setHttpAuthUsernamePassword(String p0, String p1, String p2, String p3){} + public void setInitialScale(int p0){} + public void setLayerType(int p0, Paint p1){} + public void setLayoutParams(ViewGroup.LayoutParams p0){} + public void setMapTrackballToArrowKeys(boolean p0){} + public void setNetworkAvailable(boolean p0){} + public void setOverScrollMode(int p0){} + public void setPictureListener(WebView.PictureListener p0){} + public void setRendererPriorityPolicy(int p0, boolean p1){} + public void setScrollBarStyle(int p0){} + public void setTextClassifier(TextClassifier p0){} + public void setVerticalScrollbarOverlay(boolean p0){} + public void setWebChromeClient(WebChromeClient p0){} + public void setWebViewClient(WebViewClient p0){} + public void setWebViewRenderProcessClient(Executor p0, WebViewRenderProcessClient p1){} + public void setWebViewRenderProcessClient(WebViewRenderProcessClient p0){} + public void stopLoading(){} + public void zoomBy(float p0){} + static public class HitTestResult + { + public String getExtra(){ return null; } + public int getType(){ return 0; } + public static int ANCHOR_TYPE = 0; + public static int EDIT_TEXT_TYPE = 0; + public static int EMAIL_TYPE = 0; + public static int GEO_TYPE = 0; + public static int IMAGE_ANCHOR_TYPE = 0; + public static int IMAGE_TYPE = 0; + public static int PHONE_TYPE = 0; + public static int SRC_ANCHOR_TYPE = 0; + public static int SRC_IMAGE_ANCHOR_TYPE = 0; + public static int UNKNOWN_TYPE = 0; } - - public boolean overlayVerticalScrollbar() { - return false; + static public interface FindListener + { + void onFindResultReceived(int p0, int p1, boolean p2); } - - public void savePassword(String host, String username, String password) {} - - public void setHttpAuthUsernamePassword(String host, String realm, String username, - String password) {} - - public String[] getHttpAuthUsernamePassword(String host, String realm) { - return null; - } - - public void destroy() {} - - public static void enablePlatformNotifications() {} - - public static void disablePlatformNotifications() {} - - public void loadUrl(String url) {} - - public void loadData(String data, String mimeType, String encoding) {} - - public void loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, - String failUrl) {} - - public void stopLoading() {} - - public void reload() {} - - public boolean canGoBack() { - return false; - } - - public void goBack() {} - - public boolean canGoForward() { - return false; - } - - public void goForward() {} - - public boolean canGoBackOrForward(int steps) { - return false; - } - - public void goBackOrForward(int steps) {} - - public boolean pageUp(boolean top) { - return false; - } - - public boolean pageDown(boolean bottom) { - return false; - } - - public void clearView() {} - - public float getScale() { - return 0; - } - - public void setInitialScale(int scaleInPercent) {} - - public void invokeZoomPicker() {} - - public String getUrl() { - return null; - } - - public String getTitle() { - return null; - } - - public int getProgress() { - return 0; - } - - public int getContentHeight() { - return 0; - } - - public void pauseTimers() {} - - public void resumeTimers() {} - - public void clearCache() {} - - public void clearFormData() {} - - public void clearHistory() {} - - public void clearSslPreferences() {} - - public static String findAddress(String addr) { - return null; - } - - public void setWebViewClient(WebViewClient client) {} - - public void addJavascriptInterface(Object obj, String interfaceName) {} - - public View getZoomControls() { - return null; - } - - public boolean zoomIn() { - return false; - } - - public boolean zoomOut() { - return false; - } - - public WebSettings getSettings() { - return null; + static public interface PictureListener + { + void onNewPicture(WebView p0, Picture p1); } } diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebViewClient.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebViewClient.java index 03a98480210..e63cf85c9c8 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebViewClient.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebViewClient.java @@ -1,231 +1,66 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Generated automatically from android.webkit.WebViewClient for testing purposes + package android.webkit; -public class WebViewClient { - /** - * Give the host application a chance to take over the control when a new url is - * about to be loaded in the current WebView. If WebViewClient is not provided, - * by default WebView will ask Activity Manager to choose the proper handler for - * the url. If WebViewClient is provided, return {@code true} means the host - * application handles the url, while return {@code false} means the current - * WebView handles the url. This method is not called for requests using the - * POST "method". - * - * @param view The WebView that is initiating the callback. - * @param url The url to be loaded. - * @return {@code true} if the host application wants to leave the current - * WebView and handle the url itself, otherwise return {@code false}. - * @deprecated Use {@link #shouldOverrideUrlLoading(WebView, WebResourceRequest) - * shouldOverrideUrlLoading(WebView, WebResourceRequest)} instead. - */ - @Deprecated - public boolean shouldOverrideUrlLoading(WebView view, String url) { - return false; - } - - /** - * Give the host application a chance to take over the control when a new url is - * about to be loaded in the current WebView. If WebViewClient is not provided, - * by default WebView will ask Activity Manager to choose the proper handler for - * the url. If WebViewClient is provided, return {@code true} means the host - * application handles the url, while return {@code false} means the current - * WebView handles the url. - * - *

    - * Notes: - *

      - *
    • This method is not called for requests using the POST - * "method".
    • - *
    • This method is also called for subframes with non-http schemes, thus it - * is strongly disadvised to unconditionally call - * {@link WebView#loadUrl(String)} with the request's url from inside the method - * and then return {@code true}, as this will make WebView to attempt loading a - * non-http url, and thus fail.
    • - *
    - * - * @param view The WebView that is initiating the callback. - * @param request Object containing the details of the request. - * @return {@code true} if the host application wants to leave the current - * WebView and handle the url itself, otherwise return {@code false}. - */ - public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { - return false; - } - - /** - * Notify the host application that a page has finished loading. This method is - * called only for main frame. When onPageFinished() is called, the rendering - * picture may not be updated yet. To get the notification for the new Picture, - * use {@link WebView.PictureListener#onNewPicture}. - * - * @param view The WebView that is initiating the callback. - * @param url The url of the page. - */ - public void onPageFinished(WebView view, String url) { - } - - /** - * Notify the host application that the WebView will load the resource specified - * by the given url. - * - * @param view The WebView that is initiating the callback. - * @param url The url of the resource the WebView will load. - */ - public void onLoadResource(WebView view, String url) { - } - - /** - * Notify the host application that {@link android.webkit.WebView} content left - * over from previous page navigations will no longer be drawn. - * - *

    - * This callback can be used to determine the point at which it is safe to make - * a recycled {@link android.webkit.WebView} visible, ensuring that no stale - * content is shown. It is called at the earliest point at which it can be - * guaranteed that {@link WebView#onDraw} will no longer draw any content from - * previous navigations. The next draw will display either the - * {@link WebView#setBackgroundColor background color} of the {@link WebView}, - * or some of the contents of the newly loaded page. - * - *

    - * This method is called when the body of the HTTP response has started loading, - * is reflected in the DOM, and will be visible in subsequent draws. This - * callback occurs early in the document loading process, and as such you should - * expect that linked resources (for example, CSS and images) may not be - * available. - * - *

    - * For more fine-grained notification of visual state updates, see - * {@link WebView#postVisualStateCallback}. - * - *

    - * Please note that all the conditions and recommendations applicable to - * {@link WebView#postVisualStateCallback} also apply to this API. - * - *

    - * This callback is only called for main frame navigations. - * - * @param view The {@link android.webkit.WebView} for which the navigation - * occurred. - * @param url The URL corresponding to the page navigation that triggered this - * callback. - */ - public void onPageCommitVisible(WebView view, String url) { - } - - /** - * Notify the host application of a resource request and allow the application - * to return the data. If the return value is {@code null}, the WebView will - * continue to load the resource as usual. Otherwise, the return response and - * data will be used. - * - *

    - * This callback is invoked for a variety of URL schemes (e.g., - * {@code http(s):}, {@code - * data:}, {@code file:}, etc.), not only those schemes which send requests over - * the network. This is not called for {@code javascript:} URLs, {@code blob:} - * URLs, or for assets accessed via {@code file:///android_asset/} or - * {@code file:///android_res/} URLs. - * - *

    - * In the case of redirects, this is only called for the initial resource URL, - * not any subsequent redirect URLs. - * - *

    - * Note: This method is called on a thread other than the UI thread so - * clients should exercise caution when accessing private data or the view - * system. - * - *

    - * Note: When Safe Browsing is enabled, these URLs still undergo Safe - * Browsing checks. If this is undesired, whitelist the URL with - * {@link WebView#setSafeBrowsingWhitelist} or ignore the warning with - * {@link #onSafeBrowsingHit}. - * - * @param view The {@link android.webkit.WebView} that is requesting the - * resource. - * @param url The raw url of the resource. - * @return A {@link android.webkit.WebResourceResponse} containing the response - * information or {@code null} if the WebView should load the resource - * itself. - * @deprecated Use {@link #shouldInterceptRequest(WebView, WebResourceRequest) - * shouldInterceptRequest(WebView, WebResourceRequest)} instead. - */ - @Deprecated - public WebResourceResponse shouldInterceptRequest(WebView view, String url) { - return null; - } - - /** - * Notify the host application of a resource request and allow the application - * to return the data. If the return value is {@code null}, the WebView will - * continue to load the resource as usual. Otherwise, the return response and - * data will be used. - * - *

    - * This callback is invoked for a variety of URL schemes (e.g., - * {@code http(s):}, {@code - * data:}, {@code file:}, etc.), not only those schemes which send requests over - * the network. This is not called for {@code javascript:} URLs, {@code blob:} - * URLs, or for assets accessed via {@code file:///android_asset/} or - * {@code file:///android_res/} URLs. - * - *

    - * In the case of redirects, this is only called for the initial resource URL, - * not any subsequent redirect URLs. - * - *

    - * Note: This method is called on a thread other than the UI thread so - * clients should exercise caution when accessing private data or the view - * system. - * - *

    - * Note: When Safe Browsing is enabled, these URLs still undergo Safe - * Browsing checks. If this is undesired, whitelist the URL with - * {@link WebView#setSafeBrowsingWhitelist} or ignore the warning with - * {@link #onSafeBrowsingHit}. - * - * @param view The {@link android.webkit.WebView} that is requesting the - * resource. - * @param request Object containing the details of the request. - * @return A {@link android.webkit.WebResourceResponse} containing the response - * information or {@code null} if the WebView should load the resource - * itself. - */ - public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { - return null; - } - - /** - * Report an error to the host application. These errors are unrecoverable (i.e. - * the main resource is unavailable). The {@code errorCode} parameter - * corresponds to one of the {@code ERROR_*} constants. - * - * @param view The WebView that is initiating the callback. - * @param errorCode The error code corresponding to an ERROR_* value. - * @param description A String describing the error. - * @param failingUrl The url that failed to load. - * @deprecated Use - * {@link #onReceivedError(WebView, WebResourceRequest, WebResourceError) - * onReceivedError(WebView, WebResourceRequest, WebResourceError)} - * instead. - */ - @Deprecated - public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { - } +import android.graphics.Bitmap; +import android.net.http.SslError; +import android.os.Message; +import android.view.KeyEvent; +import android.webkit.ClientCertRequest; +import android.webkit.HttpAuthHandler; +import android.webkit.RenderProcessGoneDetail; +import android.webkit.SafeBrowsingResponse; +import android.webkit.SslErrorHandler; +import android.webkit.WebResourceError; +import android.webkit.WebResourceRequest; +import android.webkit.WebResourceResponse; +import android.webkit.WebView; +public class WebViewClient +{ + public WebResourceResponse shouldInterceptRequest(WebView p0, String p1){ return null; } + public WebResourceResponse shouldInterceptRequest(WebView p0, WebResourceRequest p1){ return null; } + public WebViewClient(){} + public boolean onRenderProcessGone(WebView p0, RenderProcessGoneDetail p1){ return false; } + public boolean shouldOverrideKeyEvent(WebView p0, KeyEvent p1){ return false; } + public boolean shouldOverrideUrlLoading(WebView p0, String p1){ return false; } + public boolean shouldOverrideUrlLoading(WebView p0, WebResourceRequest p1){ return false; } + public static int ERROR_AUTHENTICATION = 0; + public static int ERROR_BAD_URL = 0; + public static int ERROR_CONNECT = 0; + public static int ERROR_FAILED_SSL_HANDSHAKE = 0; + public static int ERROR_FILE = 0; + public static int ERROR_FILE_NOT_FOUND = 0; + public static int ERROR_HOST_LOOKUP = 0; + public static int ERROR_IO = 0; + public static int ERROR_PROXY_AUTHENTICATION = 0; + public static int ERROR_REDIRECT_LOOP = 0; + public static int ERROR_TIMEOUT = 0; + public static int ERROR_TOO_MANY_REQUESTS = 0; + public static int ERROR_UNKNOWN = 0; + public static int ERROR_UNSAFE_RESOURCE = 0; + public static int ERROR_UNSUPPORTED_AUTH_SCHEME = 0; + public static int ERROR_UNSUPPORTED_SCHEME = 0; + public static int SAFE_BROWSING_THREAT_BILLING = 0; + public static int SAFE_BROWSING_THREAT_MALWARE = 0; + public static int SAFE_BROWSING_THREAT_PHISHING = 0; + public static int SAFE_BROWSING_THREAT_UNKNOWN = 0; + public static int SAFE_BROWSING_THREAT_UNWANTED_SOFTWARE = 0; + public void doUpdateVisitedHistory(WebView p0, String p1, boolean p2){} + public void onFormResubmission(WebView p0, Message p1, Message p2){} + public void onLoadResource(WebView p0, String p1){} + public void onPageCommitVisible(WebView p0, String p1){} + public void onPageFinished(WebView p0, String p1){} + public void onPageStarted(WebView p0, String p1, Bitmap p2){} + public void onReceivedClientCertRequest(WebView p0, ClientCertRequest p1){} + public void onReceivedError(WebView p0, WebResourceRequest p1, WebResourceError p2){} + public void onReceivedError(WebView p0, int p1, String p2, String p3){} + public void onReceivedHttpAuthRequest(WebView p0, HttpAuthHandler p1, String p2, String p3){} + public void onReceivedHttpError(WebView p0, WebResourceRequest p1, WebResourceResponse p2){} + public void onReceivedLoginRequest(WebView p0, String p1, String p2, String p3){} + public void onReceivedSslError(WebView p0, SslErrorHandler p1, SslError p2){} + public void onSafeBrowsingHit(WebView p0, WebResourceRequest p1, int p2, SafeBrowsingResponse p3){} + public void onScaleChanged(WebView p0, float p1, float p2){} + public void onTooManyRedirects(WebView p0, Message p1, Message p2){} + public void onUnhandledKeyEvent(WebView p0, KeyEvent p1){} } diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebViewRenderProcess.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebViewRenderProcess.java new file mode 100644 index 00000000000..0c9d0d1385c --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebViewRenderProcess.java @@ -0,0 +1,10 @@ +// Generated automatically from android.webkit.WebViewRenderProcess for testing purposes + +package android.webkit; + + +abstract public class WebViewRenderProcess +{ + public WebViewRenderProcess(){} + public abstract boolean terminate(); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebViewRenderProcessClient.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebViewRenderProcessClient.java new file mode 100644 index 00000000000..742a6ed1438 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebViewRenderProcessClient.java @@ -0,0 +1,13 @@ +// Generated automatically from android.webkit.WebViewRenderProcessClient for testing purposes + +package android.webkit; + +import android.webkit.WebView; +import android.webkit.WebViewRenderProcess; + +abstract public class WebViewRenderProcessClient +{ + public WebViewRenderProcessClient(){} + public abstract void onRenderProcessResponsive(WebView p0, WebViewRenderProcess p1); + public abstract void onRenderProcessUnresponsive(WebView p0, WebViewRenderProcess p1); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/widget/AbsListView.java b/java/ql/test/stubs/google-android-9.0.0/android/widget/AbsListView.java new file mode 100644 index 00000000000..64da209cc56 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/widget/AbsListView.java @@ -0,0 +1,212 @@ +// Generated automatically from android.widget.AbsListView for testing purposes + +package android.widget; + +import android.content.Context; +import android.content.Intent; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.os.Parcelable; +import android.text.Editable; +import android.text.TextWatcher; +import android.util.AttributeSet; +import android.util.SparseBooleanArray; +import android.view.ActionMode; +import android.view.ContextMenu; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.PointerIcon; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.view.accessibility.AccessibilityNodeInfo; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputConnection; +import android.widget.Adapter; +import android.widget.AdapterView; +import android.widget.Filter; +import android.widget.ListAdapter; +import java.util.ArrayList; +import java.util.List; + +abstract public class AbsListView extends AdapterView implements Filter.FilterListener, TextWatcher, ViewTreeObserver.OnGlobalLayoutListener, ViewTreeObserver.OnTouchModeChangeListener +{ + protected AbsListView() {} + protected ContextMenu.ContextMenuInfo getContextMenuInfo(){ return null; } + protected ViewGroup.LayoutParams generateDefaultLayoutParams(){ return null; } + protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p0){ return null; } + protected boolean checkLayoutParams(ViewGroup.LayoutParams p0){ return false; } + protected boolean isInFilterMode(){ return false; } + protected boolean isPaddingOffsetRequired(){ return false; } + protected float getBottomFadingEdgeStrength(){ return 0; } + protected float getTopFadingEdgeStrength(){ return 0; } + protected int computeVerticalScrollExtent(){ return 0; } + protected int computeVerticalScrollOffset(){ return 0; } + protected int computeVerticalScrollRange(){ return 0; } + protected int getBottomPaddingOffset(){ return 0; } + protected int getLeftPaddingOffset(){ return 0; } + protected int getRightPaddingOffset(){ return 0; } + protected int getTopPaddingOffset(){ return 0; } + protected void dispatchDraw(Canvas p0){} + protected void dispatchSetPressed(boolean p0){} + protected void drawableStateChanged(){} + protected void handleDataChanged(){} + protected void layoutChildren(){} + protected void onAttachedToWindow(){} + protected void onDetachedFromWindow(){} + protected void onDisplayHint(int p0){} + protected void onFocusChanged(boolean p0, int p1, Rect p2){} + protected void onLayout(boolean p0, int p1, int p2, int p3, int p4){} + protected void onMeasure(int p0, int p1){} + protected void onOverScrolled(int p0, int p1, boolean p2, boolean p3){} + protected void onSizeChanged(int p0, int p1, int p2, int p3){} + public AbsListView(Context p0){} + public AbsListView(Context p0, AttributeSet p1){} + public AbsListView(Context p0, AttributeSet p1, int p2){} + public AbsListView(Context p0, AttributeSet p1, int p2, int p3){} + public AbsListView.LayoutParams generateLayoutParams(AttributeSet p0){ return null; } + public CharSequence getAccessibilityClassName(){ return null; } + public CharSequence getTextFilter(){ return null; } + public Drawable getSelector(){ return null; } + public InputConnection onCreateInputConnection(EditorInfo p0){ return null; } + public Parcelable onSaveInstanceState(){ return null; } + public PointerIcon onResolvePointerIcon(MotionEvent p0, int p1){ return null; } + public SparseBooleanArray getCheckedItemPositions(){ return null; } + public View getSelectedView(){ return null; } + public boolean canScrollList(int p0){ return false; } + public boolean checkInputConnectionProxy(View p0){ return false; } + public boolean hasTextFilter(){ return false; } + public boolean isDrawSelectorOnTop(){ return false; } + public boolean isFastScrollAlwaysVisible(){ return false; } + public boolean isFastScrollEnabled(){ return false; } + public boolean isItemChecked(int p0){ return false; } + public boolean isScrollingCacheEnabled(){ return false; } + public boolean isSmoothScrollbarEnabled(){ return false; } + public boolean isStackFromBottom(){ return false; } + public boolean isTextFilterEnabled(){ return false; } + public boolean onGenericMotionEvent(MotionEvent p0){ return false; } + public boolean onInterceptHoverEvent(MotionEvent p0){ return false; } + public boolean onInterceptTouchEvent(MotionEvent p0){ return false; } + public boolean onKeyDown(int p0, KeyEvent p1){ return false; } + public boolean onKeyUp(int p0, KeyEvent p1){ return false; } + public boolean onNestedFling(View p0, float p1, float p2, boolean p3){ return false; } + public boolean onRemoteAdapterConnected(){ return false; } + public boolean onStartNestedScroll(View p0, View p1, int p2){ return false; } + public boolean onTouchEvent(MotionEvent p0){ return false; } + public boolean performItemClick(View p0, int p1, long p2){ return false; } + public boolean showContextMenu(){ return false; } + public boolean showContextMenu(float p0, float p1){ return false; } + public boolean showContextMenuForChild(View p0){ return false; } + public boolean showContextMenuForChild(View p0, float p1, float p2){ return false; } + public boolean verifyDrawable(Drawable p0){ return false; } + public int getBottomEdgeEffectColor(){ return 0; } + public int getCacheColorHint(){ return 0; } + public int getCheckedItemCount(){ return 0; } + public int getCheckedItemPosition(){ return 0; } + public int getChoiceMode(){ return 0; } + public int getListPaddingBottom(){ return 0; } + public int getListPaddingLeft(){ return 0; } + public int getListPaddingRight(){ return 0; } + public int getListPaddingTop(){ return 0; } + public int getSolidColor(){ return 0; } + public int getTopEdgeEffectColor(){ return 0; } + public int getTranscriptMode(){ return 0; } + public int getVerticalScrollbarWidth(){ return 0; } + public int pointToPosition(int p0, int p1){ return 0; } + public long pointToRowId(int p0, int p1){ return 0; } + public long[] getCheckedItemIds(){ return null; } + public static int CHOICE_MODE_MULTIPLE = 0; + public static int CHOICE_MODE_MULTIPLE_MODAL = 0; + public static int CHOICE_MODE_NONE = 0; + public static int CHOICE_MODE_SINGLE = 0; + public static int TRANSCRIPT_MODE_ALWAYS_SCROLL = 0; + public static int TRANSCRIPT_MODE_DISABLED = 0; + public static int TRANSCRIPT_MODE_NORMAL = 0; + public void addTouchables(ArrayList p0){} + public void afterTextChanged(Editable p0){} + public void beforeTextChanged(CharSequence p0, int p1, int p2, int p3){} + public void clearChoices(){} + public void clearTextFilter(){} + public void deferNotifyDataSetChanged(){} + public void dispatchDrawableHotspotChanged(float p0, float p1){} + public void draw(Canvas p0){} + public void fling(int p0){} + public void getFocusedRect(Rect p0){} + public void invalidateViews(){} + public void jumpDrawablesToCurrentState(){} + public void onCancelPendingInputEvents(){} + public void onFilterComplete(int p0){} + public void onGlobalLayout(){} + public void onInitializeAccessibilityNodeInfoForItem(View p0, int p1, AccessibilityNodeInfo p2){} + public void onNestedScroll(View p0, int p1, int p2, int p3, int p4){} + public void onNestedScrollAccepted(View p0, View p1, int p2){} + public void onRemoteAdapterDisconnected(){} + public void onRestoreInstanceState(Parcelable p0){} + public void onRtlPropertiesChanged(int p0){} + public void onTextChanged(CharSequence p0, int p1, int p2, int p3){} + public void onTouchModeChanged(boolean p0){} + public void onWindowFocusChanged(boolean p0){} + public void reclaimViews(List p0){} + public void requestDisallowInterceptTouchEvent(boolean p0){} + public void requestLayout(){} + public void scrollListBy(int p0){} + public void setAdapter(ListAdapter p0){} + public void setBottomEdgeEffectColor(int p0){} + public void setCacheColorHint(int p0){} + public void setChoiceMode(int p0){} + public void setDrawSelectorOnTop(boolean p0){} + public void setEdgeEffectColor(int p0){} + public void setFastScrollAlwaysVisible(boolean p0){} + public void setFastScrollEnabled(boolean p0){} + public void setFastScrollStyle(int p0){} + public void setFilterText(String p0){} + public void setFriction(float p0){} + public void setItemChecked(int p0, boolean p1){} + public void setMultiChoiceModeListener(AbsListView.MultiChoiceModeListener p0){} + public void setOnScrollListener(AbsListView.OnScrollListener p0){} + public void setRecyclerListener(AbsListView.RecyclerListener p0){} + public void setRemoteViewsAdapter(Intent p0){} + public void setScrollBarStyle(int p0){} + public void setScrollIndicators(View p0, View p1){} + public void setScrollingCacheEnabled(boolean p0){} + public void setSelectionFromTop(int p0, int p1){} + public void setSelector(Drawable p0){} + public void setSelector(int p0){} + public void setSmoothScrollbarEnabled(boolean p0){} + public void setStackFromBottom(boolean p0){} + public void setTextFilterEnabled(boolean p0){} + public void setTopEdgeEffectColor(int p0){} + public void setTranscriptMode(int p0){} + public void setVelocityScale(float p0){} + public void setVerticalScrollbarPosition(int p0){} + public void smoothScrollBy(int p0, int p1){} + public void smoothScrollToPosition(int p0){} + public void smoothScrollToPosition(int p0, int p1){} + public void smoothScrollToPositionFromTop(int p0, int p1){} + public void smoothScrollToPositionFromTop(int p0, int p1, int p2){} + static public class LayoutParams extends ViewGroup.LayoutParams + { + protected LayoutParams() {} + public LayoutParams(Context p0, AttributeSet p1){} + public LayoutParams(ViewGroup.LayoutParams p0){} + public LayoutParams(int p0, int p1){} + public LayoutParams(int p0, int p1, int p2){} + } + static public interface MultiChoiceModeListener extends ActionMode.Callback + { + void onItemCheckedStateChanged(ActionMode p0, int p1, long p2, boolean p3); + } + static public interface OnScrollListener + { + static int SCROLL_STATE_FLING = 0; + static int SCROLL_STATE_IDLE = 0; + static int SCROLL_STATE_TOUCH_SCROLL = 0; + void onScroll(AbsListView p0, int p1, int p2, int p3); + void onScrollStateChanged(AbsListView p0, int p1); + } + static public interface RecyclerListener + { + void onMovedToScrapHeap(View p0); + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/widget/AbsoluteLayout.java b/java/ql/test/stubs/google-android-9.0.0/android/widget/AbsoluteLayout.java new file mode 100644 index 00000000000..438d8feca86 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/widget/AbsoluteLayout.java @@ -0,0 +1,23 @@ +// Generated automatically from android.widget.AbsoluteLayout for testing purposes + +package android.widget; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.ViewGroup; + +public class AbsoluteLayout extends ViewGroup +{ + protected AbsoluteLayout() {} + protected ViewGroup.LayoutParams generateDefaultLayoutParams(){ return null; } + protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p0){ return null; } + protected boolean checkLayoutParams(ViewGroup.LayoutParams p0){ return false; } + protected void onLayout(boolean p0, int p1, int p2, int p3, int p4){} + protected void onMeasure(int p0, int p1){} + public AbsoluteLayout(Context p0){} + public AbsoluteLayout(Context p0, AttributeSet p1){} + public AbsoluteLayout(Context p0, AttributeSet p1, int p2){} + public AbsoluteLayout(Context p0, AttributeSet p1, int p2, int p3){} + public ViewGroup.LayoutParams generateLayoutParams(AttributeSet p0){ return null; } + public boolean shouldDelayChildPressedState(){ return false; } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/widget/Adapter.java b/java/ql/test/stubs/google-android-9.0.0/android/widget/Adapter.java new file mode 100644 index 00000000000..06f719feab0 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/widget/Adapter.java @@ -0,0 +1,24 @@ +// Generated automatically from android.widget.Adapter for testing purposes + +package android.widget; + +import android.database.DataSetObserver; +import android.view.View; +import android.view.ViewGroup; + +public interface Adapter +{ + Object getItem(int p0); + View getView(int p0, View p1, ViewGroup p2); + boolean hasStableIds(); + boolean isEmpty(); + default CharSequence[] getAutofillOptions(){ return null; } + int getCount(); + int getItemViewType(int p0); + int getViewTypeCount(); + long getItemId(int p0); + static int IGNORE_ITEM_VIEW_TYPE = 0; + static int NO_SELECTION = 0; + void registerDataSetObserver(DataSetObserver p0); + void unregisterDataSetObserver(DataSetObserver p0); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/widget/AdapterView.java b/java/ql/test/stubs/google-android-9.0.0/android/widget/AdapterView.java new file mode 100644 index 00000000000..a071941461c --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/widget/AdapterView.java @@ -0,0 +1,77 @@ +// Generated automatically from android.widget.AdapterView for testing purposes + +package android.widget; + +import android.content.Context; +import android.os.Parcelable; +import android.util.AttributeSet; +import android.util.SparseArray; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewStructure; +import android.widget.Adapter; + +abstract public class AdapterView extends ViewGroup +{ + protected AdapterView() {} + protected boolean canAnimate(){ return false; } + protected void dispatchRestoreInstanceState(SparseArray p0){} + protected void dispatchSaveInstanceState(SparseArray p0){} + protected void onDetachedFromWindow(){} + protected void onLayout(boolean p0, int p1, int p2, int p3, int p4){} + public AdapterView(Context p0){} + public AdapterView(Context p0, AttributeSet p1){} + public AdapterView(Context p0, AttributeSet p1, int p2){} + public AdapterView(Context p0, AttributeSet p1, int p2, int p3){} + public CharSequence getAccessibilityClassName(){ return null; } + public Object getItemAtPosition(int p0){ return null; } + public Object getSelectedItem(){ return null; } + public View getEmptyView(){ return null; } + public abstract T getAdapter(); + public abstract View getSelectedView(); + public abstract void setAdapter(T p0); + public abstract void setSelection(int p0); + public boolean performItemClick(View p0, int p1, long p2){ return false; } + public final AdapterView.OnItemClickListener getOnItemClickListener(){ return null; } + public final AdapterView.OnItemLongClickListener getOnItemLongClickListener(){ return null; } + public final AdapterView.OnItemSelectedListener getOnItemSelectedListener(){ return null; } + public int getCount(){ return 0; } + public int getFirstVisiblePosition(){ return 0; } + public int getLastVisiblePosition(){ return 0; } + public int getPositionForView(View p0){ return 0; } + public int getSelectedItemPosition(){ return 0; } + public long getItemIdAtPosition(int p0){ return 0; } + public long getSelectedItemId(){ return 0; } + public static int INVALID_POSITION = 0; + public static int ITEM_VIEW_TYPE_HEADER_OR_FOOTER = 0; + public static int ITEM_VIEW_TYPE_IGNORE = 0; + public static long INVALID_ROW_ID = 0; + public void addView(View p0){} + public void addView(View p0, ViewGroup.LayoutParams p1){} + public void addView(View p0, int p1){} + public void addView(View p0, int p1, ViewGroup.LayoutParams p2){} + public void onProvideAutofillStructure(ViewStructure p0, int p1){} + public void removeAllViews(){} + public void removeView(View p0){} + public void removeViewAt(int p0){} + public void setEmptyView(View p0){} + public void setFocusable(int p0){} + public void setFocusableInTouchMode(boolean p0){} + public void setOnClickListener(View.OnClickListener p0){} + public void setOnItemClickListener(AdapterView.OnItemClickListener p0){} + public void setOnItemLongClickListener(AdapterView.OnItemLongClickListener p0){} + public void setOnItemSelectedListener(AdapterView.OnItemSelectedListener p0){} + static public interface OnItemClickListener + { + void onItemClick(AdapterView p0, View p1, int p2, long p3); + } + static public interface OnItemLongClickListener + { + boolean onItemLongClick(AdapterView p0, View p1, int p2, long p3); + } + static public interface OnItemSelectedListener + { + void onItemSelected(AdapterView p0, View p1, int p2, long p3); + void onNothingSelected(AdapterView p0); + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/widget/Button.java b/java/ql/test/stubs/google-android-9.0.0/android/widget/Button.java index 69cf1911ffd..53413c878b4 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/widget/Button.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/widget/Button.java @@ -1,42 +1,20 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ +// Generated automatically from android.widget.Button for testing purposes package android.widget; import android.content.Context; import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.PointerIcon; +import android.widget.TextView; -public class Button extends TextView { - public Button(Context context) { - super(null); - } - - public Button(Context context, AttributeSet attrs) { - super(null); - } - - public Button(Context context, AttributeSet attrs, int defStyleAttr) { - super(null); - } - - public Button(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(null); - } - - @Override - public CharSequence getAccessibilityClassName() { - return null; - } - +public class Button extends TextView +{ + protected Button() {} + public Button(Context p0){} + public Button(Context p0, AttributeSet p1){} + public Button(Context p0, AttributeSet p1, int p2){} + public Button(Context p0, AttributeSet p1, int p2, int p3){} + public CharSequence getAccessibilityClassName(){ return null; } + public PointerIcon onResolvePointerIcon(MotionEvent p0, int p1){ return null; } } diff --git a/java/ql/test/stubs/google-android-9.0.0/android/widget/Filter.java b/java/ql/test/stubs/google-android-9.0.0/android/widget/Filter.java new file mode 100644 index 00000000000..f0572f12d78 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/widget/Filter.java @@ -0,0 +1,24 @@ +// Generated automatically from android.widget.Filter for testing purposes + +package android.widget; + + +abstract public class Filter +{ + protected abstract Filter.FilterResults performFiltering(CharSequence p0); + protected abstract void publishResults(CharSequence p0, Filter.FilterResults p1); + public CharSequence convertResultToString(Object p0){ return null; } + public Filter(){} + public final void filter(CharSequence p0){} + public final void filter(CharSequence p0, Filter.FilterListener p1){} + static class FilterResults + { + public FilterResults(){} + public Object values = null; + public int count = 0; + } + static public interface FilterListener + { + void onFilterComplete(int p0); + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/widget/FrameLayout.java b/java/ql/test/stubs/google-android-9.0.0/android/widget/FrameLayout.java new file mode 100644 index 00000000000..6901b838087 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/widget/FrameLayout.java @@ -0,0 +1,40 @@ +// Generated automatically from android.widget.FrameLayout for testing purposes + +package android.widget; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.ViewGroup; + +public class FrameLayout extends ViewGroup +{ + protected FrameLayout() {} + protected FrameLayout.LayoutParams generateDefaultLayoutParams(){ return null; } + protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p0){ return null; } + protected boolean checkLayoutParams(ViewGroup.LayoutParams p0){ return false; } + protected void onLayout(boolean p0, int p1, int p2, int p3, int p4){} + protected void onMeasure(int p0, int p1){} + public CharSequence getAccessibilityClassName(){ return null; } + public FrameLayout(Context p0){} + public FrameLayout(Context p0, AttributeSet p1){} + public FrameLayout(Context p0, AttributeSet p1, int p2){} + public FrameLayout(Context p0, AttributeSet p1, int p2, int p3){} + public FrameLayout.LayoutParams generateLayoutParams(AttributeSet p0){ return null; } + public boolean getConsiderGoneChildrenWhenMeasuring(){ return false; } + public boolean getMeasureAllChildren(){ return false; } + public boolean shouldDelayChildPressedState(){ return false; } + public void setForegroundGravity(int p0){} + public void setMeasureAllChildren(boolean p0){} + static public class LayoutParams extends ViewGroup.MarginLayoutParams + { + protected LayoutParams() {} + public LayoutParams(Context p0, AttributeSet p1){} + public LayoutParams(FrameLayout.LayoutParams p0){} + public LayoutParams(ViewGroup.LayoutParams p0){} + public LayoutParams(ViewGroup.MarginLayoutParams p0){} + public LayoutParams(int p0, int p1){} + public LayoutParams(int p0, int p1, int p2){} + public int gravity = 0; + public static int UNSPECIFIED_GRAVITY = 0; + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/widget/ListAdapter.java b/java/ql/test/stubs/google-android-9.0.0/android/widget/ListAdapter.java new file mode 100644 index 00000000000..c23fea65b27 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/widget/ListAdapter.java @@ -0,0 +1,11 @@ +// Generated automatically from android.widget.ListAdapter for testing purposes + +package android.widget; + +import android.widget.Adapter; + +public interface ListAdapter extends Adapter +{ + boolean areAllItemsEnabled(); + boolean isEnabled(int p0); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/widget/ListView.java b/java/ql/test/stubs/google-android-9.0.0/android/widget/ListView.java new file mode 100644 index 00000000000..088efbc883f --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/widget/ListView.java @@ -0,0 +1,73 @@ +// Generated automatically from android.widget.ListView for testing purposes + +package android.widget; + +import android.content.Context; +import android.content.Intent; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.KeyEvent; +import android.view.View; +import android.view.accessibility.AccessibilityNodeInfo; +import android.widget.AbsListView; +import android.widget.ListAdapter; + +public class ListView extends AbsListView +{ + protected ListView() {} + protected boolean canAnimate(){ return false; } + protected boolean drawChild(Canvas p0, View p1, long p2){ return false; } + protected void dispatchDraw(Canvas p0){} + protected void layoutChildren(){} + protected void onDetachedFromWindow(){} + protected void onFinishInflate(){} + protected void onFocusChanged(boolean p0, int p1, Rect p2){} + protected void onMeasure(int p0, int p1){} + protected void onSizeChanged(int p0, int p1, int p2, int p3){} + public CharSequence getAccessibilityClassName(){ return null; } + public Drawable getDivider(){ return null; } + public Drawable getOverscrollFooter(){ return null; } + public Drawable getOverscrollHeader(){ return null; } + public ListAdapter getAdapter(){ return null; } + public ListView(Context p0){} + public ListView(Context p0, AttributeSet p1){} + public ListView(Context p0, AttributeSet p1, int p2){} + public ListView(Context p0, AttributeSet p1, int p2, int p3){} + public boolean areFooterDividersEnabled(){ return false; } + public boolean areHeaderDividersEnabled(){ return false; } + public boolean dispatchKeyEvent(KeyEvent p0){ return false; } + public boolean getItemsCanFocus(){ return false; } + public boolean isOpaque(){ return false; } + public boolean onKeyDown(int p0, KeyEvent p1){ return false; } + public boolean onKeyMultiple(int p0, int p1, KeyEvent p2){ return false; } + public boolean onKeyUp(int p0, KeyEvent p1){ return false; } + public boolean removeFooterView(View p0){ return false; } + public boolean removeHeaderView(View p0){ return false; } + public boolean requestChildRectangleOnScreen(View p0, Rect p1, boolean p2){ return false; } + public int getDividerHeight(){ return 0; } + public int getFooterViewsCount(){ return 0; } + public int getHeaderViewsCount(){ return 0; } + public int getMaxScrollAmount(){ return 0; } + public long[] getCheckItemIds(){ return null; } + public void addFooterView(View p0){} + public void addFooterView(View p0, Object p1, boolean p2){} + public void addHeaderView(View p0){} + public void addHeaderView(View p0, Object p1, boolean p2){} + public void onInitializeAccessibilityNodeInfoForItem(View p0, int p1, AccessibilityNodeInfo p2){} + public void setAdapter(ListAdapter p0){} + public void setCacheColorHint(int p0){} + public void setDivider(Drawable p0){} + public void setDividerHeight(int p0){} + public void setFooterDividersEnabled(boolean p0){} + public void setHeaderDividersEnabled(boolean p0){} + public void setItemsCanFocus(boolean p0){} + public void setOverscrollFooter(Drawable p0){} + public void setOverscrollHeader(Drawable p0){} + public void setRemoteViewsAdapter(Intent p0){} + public void setSelection(int p0){} + public void setSelectionAfterHeaderView(){} + public void smoothScrollByOffset(int p0){} + public void smoothScrollToPosition(int p0){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/widget/SpinnerAdapter.java b/java/ql/test/stubs/google-android-9.0.0/android/widget/SpinnerAdapter.java new file mode 100644 index 00000000000..9a389615336 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/widget/SpinnerAdapter.java @@ -0,0 +1,12 @@ +// Generated automatically from android.widget.SpinnerAdapter for testing purposes + +package android.widget; + +import android.view.View; +import android.view.ViewGroup; +import android.widget.Adapter; + +public interface SpinnerAdapter extends Adapter +{ + View getDropDownView(int p0, View p1, ViewGroup p2); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/widget/TextView.java b/java/ql/test/stubs/google-android-9.0.0/android/widget/TextView.java index 7ff5e1f7143..491cf47514a 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/widget/TextView.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/widget/TextView.java @@ -1,816 +1,383 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ +// Generated automatically from android.widget.TextView for testing purposes package android.widget; -import java.util.ArrayList; -import java.util.Locale; -import android.annotation.Nullable; import android.content.Context; import android.content.res.ColorStateList; -import android.content.res.TypedArray; +import android.content.res.Configuration; import android.graphics.BlendMode; +import android.graphics.Canvas; import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.LocaleList; -import android.os.UserHandle; +import android.os.Parcelable; +import android.text.Editable; +import android.text.InputFilter; +import android.text.Layout; +import android.text.PrecomputedText; +import android.text.Spannable; +import android.text.TextDirectionHeuristic; +import android.text.TextPaint; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.text.method.KeyListener; +import android.text.method.MovementMethod; +import android.text.method.TransformationMethod; +import android.text.style.URLSpan; import android.util.AttributeSet; +import android.view.ActionMode; +import android.view.ContentInfo; +import android.view.ContextMenu; +import android.view.DragEvent; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.PointerIcon; import android.view.View; - -public class TextView extends View { - public @interface XMLTypefaceAttr { - - } - public @interface AutoSizeTextType { - } - - public static void preloadFontCache() {} - - public TextView(Context context) { - super(null); - } - - public TextView(Context context, AttributeSet attrs) { - super(null); - } - - public TextView(Context context, AttributeSet attrs, int defStyleAttr) { - super(null); - } - - public TextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(null); - } - - public void setAutoSizeTextTypeWithDefaults(int autoSizeTextType) {} - - public void setAutoSizeTextTypeUniformWithConfiguration(int autoSizeMinTextSize, - int autoSizeMaxTextSize, int autoSizeStepGranularity, int unit) {} - - public void setAutoSizeTextTypeUniformWithPresetSizes(int[] presetSizes, int unit) {} - - public int getAutoSizeTextType() { - return 0; - } - - public int getAutoSizeStepGranularity() { - return 0; - } - - public int getAutoSizeMinTextSize() { - return 0; - } - - public int getAutoSizeMaxTextSize() { - return 0; - } - - public int[] getAutoSizeTextAvailableSizes() { - return null; - } - - public void setTypeface(Typeface tf, int style) {} - - public CharSequence getText() { - return null; - } - - public int length() { - return 0; - } - - public CharSequence getTransformed() { - return null; - } - - public int getLineHeight() { - return 0; - } - - public int getCompoundPaddingTop() { - return 0; - } - - public int getCompoundPaddingBottom() { - return 0; - } - - public int getCompoundPaddingLeft() { - return 0; - } - - public int getCompoundPaddingRight() { - return 0; - } - - public int getCompoundPaddingStart() { - return 0; - } - - public int getCompoundPaddingEnd() { - return 0; - } - - public int getExtendedPaddingTop() { - return 0; - } - - public int getExtendedPaddingBottom() { - return 0; - } - - public int getTotalPaddingLeft() { - return 0; - } - - public int getTotalPaddingRight() { - return 0; - } - - public int getTotalPaddingStart() { - return 0; - } - - public int getTotalPaddingEnd() { - return 0; - } - - public int getTotalPaddingTop() { - return 0; - } - - public int getTotalPaddingBottom() { - return 0; - } - - public void setCompoundDrawables(Drawable left, Drawable top, Drawable right, Drawable bottom) {} - - public void setCompoundDrawablesWithIntrinsicBounds(int left, int top, int right, int bottom) {} - - public void setCompoundDrawablesWithIntrinsicBounds(Drawable left, Drawable top, Drawable right, - Drawable bottom) {} - - public void setCompoundDrawablesRelative(Drawable start, Drawable top, Drawable end, - Drawable bottom) {} - - public void setCompoundDrawablesRelativeWithIntrinsicBounds(int start, int top, int end, - int bottom) {} - - public void setCompoundDrawablesRelativeWithIntrinsicBounds(Drawable start, Drawable top, - Drawable end, Drawable bottom) {} - - public Drawable[] getCompoundDrawables() { - return null; - } - - public Drawable[] getCompoundDrawablesRelative() { - return null; - } - - public void setCompoundDrawablePadding(int pad) {} - - public int getCompoundDrawablePadding() { - return 0; - } - - public void setCompoundDrawableTintList(ColorStateList tint) {} - - public ColorStateList getCompoundDrawableTintList() { - return null; - } - - public void setCompoundDrawableTintMode(PorterDuff.Mode tintMode) {} - - public void setCompoundDrawableTintBlendMode(BlendMode blendMode) {} - - public PorterDuff.Mode getCompoundDrawableTintMode() { - return null; - } - - public BlendMode getCompoundDrawableTintBlendMode() { - return null; - } - - public void setFirstBaselineToTopHeight(int firstBaselineToTopHeight) {} - - public void setLastBaselineToBottomHeight(int lastBaselineToBottomHeight) {} - - public int getFirstBaselineToTopHeight() { - return 0; - } - - public int getLastBaselineToBottomHeight() { - return 0; - } - - public final int getAutoLinkMask() { - return 0; - } - - public void setTextSelectHandle(Drawable textSelectHandle) {} - - public void setTextSelectHandle(int textSelectHandle) {} - - @Nullable - public Drawable getTextSelectHandle() { - return null; - } - - public void setTextSelectHandleLeft(Drawable textSelectHandleLeft) {} - - public void setTextSelectHandleLeft(int textSelectHandleLeft) {} - - @Nullable - public Drawable getTextSelectHandleLeft() { - return null; - } - - public void setTextSelectHandleRight(Drawable textSelectHandleRight) {} - - public void setTextSelectHandleRight(int textSelectHandleRight) {} - - @Nullable - public Drawable getTextSelectHandleRight() { - return null; - } - - public void setTextCursorDrawable(Drawable textCursorDrawable) {} - - public void setTextCursorDrawable(int textCursorDrawable) {} - - @Nullable - public Drawable getTextCursorDrawable() { - return null; - } - - public void setTextAppearance(int resId) {} - - public void setTextAppearance(Context context, int resId) {} - - public Locale getTextLocale() { - return null; - } - - public LocaleList getTextLocales() { - return null; - } - - public void setTextLocale(Locale locale) {} - - public void setTextLocales(LocaleList locales) {} - - public float getTextSize() { - return 0; - } - - public float getScaledTextSize() { - return 0; - } - - public int getTypefaceStyle() { - return 0; - } - - public void setTextSize(float size) {} - - public void setTextSize(int unit, float size) {} - - public int getTextSizeUnit() { - return 0; - } - - public float getTextScaleX() { - return 0; - } - - public void setTextScaleX(float size) {} - - public void setTypeface(Typeface tf) {} - - public Typeface getTypeface() { - return null; - } - - public void setElegantTextHeight(boolean elegant) {} - - public void setFallbackLineSpacing(boolean enabled) {} - - public boolean isFallbackLineSpacing() { - return false; - } - - public boolean isElegantTextHeight() { - return false; - } - - public float getLetterSpacing() { - return 0; - } - - public void setLetterSpacing(float letterSpacing) {} - - public String getFontFeatureSettings() { - return null; - } - - public String getFontVariationSettings() { - return null; - } - - public void setBreakStrategy(int breakStrategy) {} - - public int getBreakStrategy() { - return 0; - } - - public void setHyphenationFrequency(int hyphenationFrequency) {} - - public int getHyphenationFrequency() { - return 0; - } - - public void setJustificationMode(int justificationMode) {} - - public int getJustificationMode() { - return 0; - } - - public void setFontFeatureSettings(String fontFeatureSettings) {} - - public boolean setFontVariationSettings(String fontVariationSettings) { - return false; - } - - public void setTextColor(int color) {} - - public void setTextColor(ColorStateList colors) {} - - public final ColorStateList getTextColors() { - return null; - } - - public final int getCurrentTextColor() { - return 0; - } - - public void setHighlightColor(int color) {} - - public int getHighlightColor() { - return 0; - } - - public final void setShowSoftInputOnFocus(boolean show) {} - - public final boolean getShowSoftInputOnFocus() { - return false; - } - - public void setShadowLayer(float radius, float dx, float dy, int color) {} - - public float getShadowRadius() { - return 0; - } - - public float getShadowDx() { - return 0; - } - - public float getShadowDy() { - return 0; - } - - public int getShadowColor() { - return 0; - } - - public final void setAutoLinkMask(int mask) {} - - public final void setLinksClickable(boolean whether) {} - - public final boolean getLinksClickable() { - return false; - } - - public final void setHintTextColor(int color) {} - - public final void setHintTextColor(ColorStateList colors) {} - - public final ColorStateList getHintTextColors() { - return null; - } - - public final int getCurrentHintTextColor() { - return 0; - } - - public final void setLinkTextColor(int color) {} - - public final void setLinkTextColor(ColorStateList colors) {} - - public final ColorStateList getLinkTextColors() { - return null; - } - - public void setGravity(int gravity) {} - - public int getGravity() { - return 0; - } - - public int getPaintFlags() { - return 0; - } - - public void setPaintFlags(int flags) {} - - public void setHorizontallyScrolling(boolean whether) {} - - public final boolean isHorizontallyScrollable() { - return false; - } - - public boolean getHorizontallyScrolling() { - return false; - } - - public void setMinLines(int minLines) {} - - public int getMinLines() { - return 0; - } - - public void setMinHeight(int minPixels) {} - - public int getMinHeight() { - return 0; - } - - public void setMaxLines(int maxLines) {} - - public int getMaxLines() { - return 0; - } - - public void setMaxHeight(int maxPixels) {} - - public int getMaxHeight() { - return 0; - } - - public void setLines(int lines) {} - - public void setHeight(int pixels) {} - - public void setMinEms(int minEms) {} - - public int getMinEms() { - return 0; - } - - public void setMinWidth(int minPixels) {} - - public int getMinWidth() { - return 0; - } - - public void setMaxEms(int maxEms) {} - - public int getMaxEms() { - return 0; - } - - public void setMaxWidth(int maxPixels) {} - - public int getMaxWidth() { - return 0; - } - - public void setEms(int ems) {} - - public void setWidth(int pixels) {} - - public void setLineSpacing(float add, float mult) {} - - public float getLineSpacingMultiplier() { - return 0; - } - - public float getLineSpacingExtra() { - return 0; - } - - public void setLineHeight(int lineHeight) {} - - public final void append(CharSequence text) {} - - public void append(CharSequence text, int start, int end) {} - - - public void setFreezesText(boolean freezesText) {} - - public boolean getFreezesText() { - return false; - } - - public final void setText(CharSequence text) {} - - public final void setTextKeepState(CharSequence text) {} - - public void setText(CharSequence text, BufferType type) {} - - public final void setText(char[] text, int start, int len) {} - - public final void setTextKeepState(CharSequence text, BufferType type) {} - - public final void setText(int resid) {} - - public final void setText(int resid, BufferType type) {} - - public final void setHint(CharSequence hint) {} - - public final void setHint(int resid) {} - - public CharSequence getHint() { - return null; - } - - public boolean isSingleLine() { - return false; - } - - public void setInputType(int type) {} - - public boolean isAnyPasswordInputType() { - return false; - } - - public void setRawInputType(int type) {} - - public int getInputType() { - return 0; - } - - public void setImeOptions(int imeOptions) {} - - public int getImeOptions() { - return 0; - } - - public void setImeActionLabel(CharSequence label, int actionId) {} - - public CharSequence getImeActionLabel() { - return null; - } - - public int getImeActionId() { - return 0; - } - - public void onEditorAction(int actionCode) {} - - public void setPrivateImeOptions(String type) {} - - public String getPrivateImeOptions() { - return null; - } - - public Bundle getInputExtras(boolean create) { - return null; - } - - public void setImeHintLocales(LocaleList hintLocales) {} - - public LocaleList getImeHintLocales() { - return null; - } - - public CharSequence getError() { - return null; - } - - public void setError(CharSequence error) {} - - public void setError(CharSequence error, Drawable icon) {} - - public boolean isTextSelectable() { - return false; - } - - public void setTextIsSelectable(boolean selectable) {} - - public int getHorizontalOffsetForDrawables() { - return 0; - } - - public int getLineCount() { - return 0; - } - - public int getLineBounds(int line, Rect bounds) { - return 0; - } - - public int getBaseline() { - return 0; - } - - public void resetErrorChangedFlag() {} - - public void hideErrorIfUnchanged() {} - - public boolean onCheckIsTextEditor() { - return false; - } - - public void beginBatchEdit() {} - - public void endBatchEdit() {} - - public void onBeginBatchEdit() {} - - public void onEndBatchEdit() {} - - public boolean onPrivateIMECommand(String action, Bundle data) { - return false; - } - - public void nullLayouts() {} - - public boolean useDynamicLayout() { - return false; - } - - public void setIncludeFontPadding(boolean includepad) {} - - public boolean getIncludeFontPadding() { - return false; - } - - public boolean bringPointIntoView(int offset) { - return false; - } - - public boolean moveCursorToVisibleOffset() { - return false; - } - - public void computeScroll() {} - - public void debug(int depth) {} - - public int getSelectionStart() { - return 0; - } - - public int getSelectionEnd() { - return 0; - } - - public boolean hasSelection() { - return false; - } - - public void setSingleLine() {} - - public void setAllCaps(boolean allCaps) {} - - public boolean isAllCaps() { - return false; - } - - public void setSingleLine(boolean singleLine) {} - - public void setMarqueeRepeatLimit(int marqueeLimit) {} - - public int getMarqueeRepeatLimit() { - return 0; - } - - public void setSelectAllOnFocus(boolean selectAllOnFocus) {} - - public void setCursorVisible(boolean visible) {} - - public boolean isCursorVisible() { - return false; - } - - public void onWindowFocusChanged(boolean hasWindowFocus) {} - - public void clearComposingText() {} - - public void setSelected(boolean selected) {} - - public boolean showContextMenu() { - return false; - } - - public boolean showContextMenu(float x, float y) { - return false; - } - - public boolean didTouchFocusSelect() { - return false; - } - - public void cancelLongPress() {} - - public void findViewsWithText(ArrayList outViews, CharSequence searched, int flags) {} - - public enum BufferType { - } - - public static ColorStateList getTextColors(Context context, TypedArray attrs) { - return null; - } - - public static int getTextColor(Context context, TypedArray attrs, int def) { - return 0; - } - - public final void setTextOperationUser(UserHandle user) {} - - public Locale getTextServicesLocale() { - return null; - } - - public boolean isInExtractedMode() { - return false; - } - - public Locale getSpellCheckerLocale() { - return null; - } - - public CharSequence getAccessibilityClassName() { - return null; - } - - public boolean isPositionVisible(final float positionX, final float positionY) { - return false; - } - - public boolean performAccessibilityActionInternal(int action, Bundle arguments) { - return false; - } - - public void sendAccessibilityEventInternal(int eventType) {} - - public boolean isInputMethodTarget() { - return false; - } - - public boolean onTextContextMenuItem(int id) { - return false; - } - - public boolean performLongClick() { - return false; - } - - public boolean isSuggestionsEnabled() { - return false; - } - - public void hideFloatingToolbar(int durationMs) {} - - public int getOffsetForPosition(float x, float y) { - return 0; - } - - public void onRtlPropertiesChanged(int layoutDirection) {} - - public void onResolveDrawables(int layoutDirection) {} - - public CharSequence getIterableTextForAccessibility() { - return null; - } - - public int getAccessibilitySelectionStart() { - return 0; - } - - public boolean isAccessibilitySelectionExtendable() { - return false; - } - - public int getAccessibilitySelectionEnd() { - return 0; - } - - public void setAccessibilitySelection(int start, int end) {} - +import android.view.ViewTreeObserver; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityNodeInfo; +import android.view.autofill.AutofillValue; +import android.view.inputmethod.CompletionInfo; +import android.view.inputmethod.CorrectionInfo; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.ExtractedText; +import android.view.inputmethod.ExtractedTextRequest; +import android.view.inputmethod.InputConnection; +import android.view.textclassifier.TextClassifier; +import android.view.translation.ViewTranslationRequest; +import android.widget.Scroller; +import java.util.ArrayList; +import java.util.Locale; +import java.util.function.Consumer; + +public class TextView extends View implements ViewTreeObserver.OnPreDrawListener +{ + protected TextView() {} + protected MovementMethod getDefaultMovementMethod(){ return null; } + protected boolean getDefaultEditable(){ return false; } + protected boolean isPaddingOffsetRequired(){ return false; } + protected boolean setFrame(int p0, int p1, int p2, int p3){ return false; } + protected boolean verifyDrawable(Drawable p0){ return false; } + protected float getLeftFadingEdgeStrength(){ return 0; } + protected float getRightFadingEdgeStrength(){ return 0; } + protected int computeHorizontalScrollRange(){ return 0; } + protected int computeVerticalScrollExtent(){ return 0; } + protected int computeVerticalScrollRange(){ return 0; } + protected int getBottomPaddingOffset(){ return 0; } + protected int getLeftPaddingOffset(){ return 0; } + protected int getRightPaddingOffset(){ return 0; } + protected int getTopPaddingOffset(){ return 0; } + protected int[] onCreateDrawableState(int p0){ return null; } + protected void drawableStateChanged(){} + protected void onAttachedToWindow(){} + protected void onConfigurationChanged(Configuration p0){} + protected void onCreateContextMenu(ContextMenu p0){} + protected void onDraw(Canvas p0){} + protected void onFocusChanged(boolean p0, int p1, Rect p2){} + protected void onLayout(boolean p0, int p1, int p2, int p3, int p4){} + protected void onMeasure(int p0, int p1){} + protected void onScrollChanged(int p0, int p1, int p2, int p3){} + protected void onSelectionChanged(int p0, int p1){} + protected void onTextChanged(CharSequence p0, int p1, int p2, int p3){} + protected void onVisibilityChanged(View p0, int p1){} + public ActionMode.Callback getCustomInsertionActionModeCallback(){ return null; } + public ActionMode.Callback getCustomSelectionActionModeCallback(){ return null; } + public AutofillValue getAutofillValue(){ return null; } + public BlendMode getCompoundDrawableTintBlendMode(){ return null; } + public Bundle getInputExtras(boolean p0){ return null; } + public CharSequence getAccessibilityClassName(){ return null; } + public CharSequence getError(){ return null; } + public CharSequence getHint(){ return null; } + public CharSequence getImeActionLabel(){ return null; } + public CharSequence getText(){ return null; } + public ColorStateList getCompoundDrawableTintList(){ return null; } + public ContentInfo onReceiveContent(ContentInfo p0){ return null; } + public Drawable getTextCursorDrawable(){ return null; } + public Drawable getTextSelectHandle(){ return null; } + public Drawable getTextSelectHandleLeft(){ return null; } + public Drawable getTextSelectHandleRight(){ return null; } + public Drawable[] getCompoundDrawables(){ return null; } + public Drawable[] getCompoundDrawablesRelative(){ return null; } + public Editable getEditableText(){ return null; } + public InputConnection onCreateInputConnection(EditorInfo p0){ return null; } + public InputFilter[] getFilters(){ return null; } + public Locale getTextLocale(){ return null; } + public LocaleList getImeHintLocales(){ return null; } + public LocaleList getTextLocales(){ return null; } + public Parcelable onSaveInstanceState(){ return null; } + public PointerIcon onResolvePointerIcon(MotionEvent p0, int p1){ return null; } + public PorterDuff.Mode getCompoundDrawableTintMode(){ return null; } + public PrecomputedText.Params getTextMetricsParams(){ return null; } + public String getFontFeatureSettings(){ return null; } + public String getFontVariationSettings(){ return null; } + public String getPrivateImeOptions(){ return null; } + public TextClassifier getTextClassifier(){ return null; } + public TextDirectionHeuristic getTextDirectionHeuristic(){ return null; } + public TextPaint getPaint(){ return null; } + public TextUtils.TruncateAt getEllipsize(){ return null; } + public TextView(Context p0){} + public TextView(Context p0, AttributeSet p1){} + public TextView(Context p0, AttributeSet p1, int p2){} + public TextView(Context p0, AttributeSet p1, int p2, int p3){} + public Typeface getTypeface(){ return null; } + public URLSpan[] getUrls(){ return null; } + public boolean bringPointIntoView(int p0){ return false; } + public boolean didTouchFocusSelect(){ return false; } + public boolean extractText(ExtractedTextRequest p0, ExtractedText p1){ return false; } + public boolean getFreezesText(){ return false; } + public boolean getIncludeFontPadding(){ return false; } + public boolean hasOverlappingRendering(){ return false; } + public boolean hasSelection(){ return false; } + public boolean isAllCaps(){ return false; } + public boolean isCursorVisible(){ return false; } + public boolean isElegantTextHeight(){ return false; } + public boolean isFallbackLineSpacing(){ return false; } + public boolean isInputMethodTarget(){ return false; } + public boolean isSingleLine(){ return false; } + public boolean isSuggestionsEnabled(){ return false; } + public boolean isTextSelectable(){ return false; } + public boolean moveCursorToVisibleOffset(){ return false; } + public boolean onCheckIsTextEditor(){ return false; } + public boolean onDragEvent(DragEvent p0){ return false; } + public boolean onGenericMotionEvent(MotionEvent p0){ return false; } + public boolean onKeyDown(int p0, KeyEvent p1){ return false; } + public boolean onKeyMultiple(int p0, int p1, KeyEvent p2){ return false; } + public boolean onKeyPreIme(int p0, KeyEvent p1){ return false; } + public boolean onKeyShortcut(int p0, KeyEvent p1){ return false; } + public boolean onKeyUp(int p0, KeyEvent p1){ return false; } + public boolean onPreDraw(){ return false; } + public boolean onPrivateIMECommand(String p0, Bundle p1){ return false; } + public boolean onTextContextMenuItem(int p0){ return false; } + public boolean onTouchEvent(MotionEvent p0){ return false; } + public boolean onTrackballEvent(MotionEvent p0){ return false; } + public boolean performLongClick(){ return false; } + public boolean setFontVariationSettings(String p0){ return false; } + public boolean showContextMenu(){ return false; } + public boolean showContextMenu(float p0, float p1){ return false; } + public final ColorStateList getHintTextColors(){ return null; } + public final ColorStateList getLinkTextColors(){ return null; } + public final ColorStateList getTextColors(){ return null; } + public final KeyListener getKeyListener(){ return null; } + public final Layout getLayout(){ return null; } + public final MovementMethod getMovementMethod(){ return null; } + public final TransformationMethod getTransformationMethod(){ return null; } + public final boolean getLinksClickable(){ return false; } + public final boolean getShowSoftInputOnFocus(){ return false; } + public final boolean isHorizontallyScrollable(){ return false; } + public final int getAutoLinkMask(){ return 0; } + public final int getCurrentHintTextColor(){ return 0; } + public final int getCurrentTextColor(){ return 0; } + public final void append(CharSequence p0){} + public final void setAutoLinkMask(int p0){} + public final void setEditableFactory(Editable.Factory p0){} + public final void setHint(CharSequence p0){} + public final void setHint(int p0){} + public final void setHintTextColor(ColorStateList p0){} + public final void setHintTextColor(int p0){} + public final void setLinkTextColor(ColorStateList p0){} + public final void setLinkTextColor(int p0){} + public final void setLinksClickable(boolean p0){} + public final void setMovementMethod(MovementMethod p0){} + public final void setShowSoftInputOnFocus(boolean p0){} + public final void setSpannableFactory(Spannable.Factory p0){} + public final void setText(CharSequence p0){} + public final void setText(char[] p0, int p1, int p2){} + public final void setText(int p0){} + public final void setText(int p0, TextView.BufferType p1){} + public final void setTextKeepState(CharSequence p0){} + public final void setTextKeepState(CharSequence p0, TextView.BufferType p1){} + public final void setTransformationMethod(TransformationMethod p0){} + public float getLetterSpacing(){ return 0; } + public float getLineSpacingExtra(){ return 0; } + public float getLineSpacingMultiplier(){ return 0; } + public float getShadowDx(){ return 0; } + public float getShadowDy(){ return 0; } + public float getShadowRadius(){ return 0; } + public float getTextScaleX(){ return 0; } + public float getTextSize(){ return 0; } + public int getAutoSizeMaxTextSize(){ return 0; } + public int getAutoSizeMinTextSize(){ return 0; } + public int getAutoSizeStepGranularity(){ return 0; } + public int getAutoSizeTextType(){ return 0; } + public int getAutofillType(){ return 0; } + public int getBaseline(){ return 0; } + public int getBreakStrategy(){ return 0; } + public int getCompoundDrawablePadding(){ return 0; } + public int getCompoundPaddingBottom(){ return 0; } + public int getCompoundPaddingEnd(){ return 0; } + public int getCompoundPaddingLeft(){ return 0; } + public int getCompoundPaddingRight(){ return 0; } + public int getCompoundPaddingStart(){ return 0; } + public int getCompoundPaddingTop(){ return 0; } + public int getExtendedPaddingBottom(){ return 0; } + public int getExtendedPaddingTop(){ return 0; } + public int getFirstBaselineToTopHeight(){ return 0; } + public int getGravity(){ return 0; } + public int getHighlightColor(){ return 0; } + public int getHyphenationFrequency(){ return 0; } + public int getImeActionId(){ return 0; } + public int getImeOptions(){ return 0; } + public int getInputType(){ return 0; } + public int getJustificationMode(){ return 0; } + public int getLastBaselineToBottomHeight(){ return 0; } + public int getLineBounds(int p0, Rect p1){ return 0; } + public int getLineCount(){ return 0; } + public int getLineHeight(){ return 0; } + public int getMarqueeRepeatLimit(){ return 0; } + public int getMaxEms(){ return 0; } + public int getMaxHeight(){ return 0; } + public int getMaxLines(){ return 0; } + public int getMaxWidth(){ return 0; } + public int getMinEms(){ return 0; } + public int getMinHeight(){ return 0; } + public int getMinLines(){ return 0; } + public int getMinWidth(){ return 0; } + public int getOffsetForPosition(float p0, float p1){ return 0; } + public int getPaintFlags(){ return 0; } + public int getSelectionEnd(){ return 0; } + public int getSelectionStart(){ return 0; } + public int getShadowColor(){ return 0; } + public int getTextSizeUnit(){ return 0; } + public int getTotalPaddingBottom(){ return 0; } + public int getTotalPaddingEnd(){ return 0; } + public int getTotalPaddingLeft(){ return 0; } + public int getTotalPaddingRight(){ return 0; } + public int getTotalPaddingStart(){ return 0; } + public int getTotalPaddingTop(){ return 0; } + public int length(){ return 0; } + public int[] getAutoSizeTextAvailableSizes(){ return null; } + public static int AUTO_SIZE_TEXT_TYPE_NONE = 0; + public static int AUTO_SIZE_TEXT_TYPE_UNIFORM = 0; + public void addExtraDataToAccessibilityNodeInfo(AccessibilityNodeInfo p0, String p1, Bundle p2){} + public void addTextChangedListener(TextWatcher p0){} + public void append(CharSequence p0, int p1, int p2){} + public void autofill(AutofillValue p0){} + public void beginBatchEdit(){} + public void cancelLongPress(){} + public void clearComposingText(){} + public void computeScroll(){} + public void debug(int p0){} + public void drawableHotspotChanged(float p0, float p1){} + public void endBatchEdit(){} + public void findViewsWithText(ArrayList p0, CharSequence p1, int p2){} + public void getFocusedRect(Rect p0){} + public void invalidateDrawable(Drawable p0){} + public void jumpDrawablesToCurrentState(){} + public void onBeginBatchEdit(){} + public void onCommitCompletion(CompletionInfo p0){} + public void onCommitCorrection(CorrectionInfo p0){} + public void onCreateViewTranslationRequest(int[] p0, Consumer p1){} + public void onEditorAction(int p0){} + public void onEndBatchEdit(){} + public void onRestoreInstanceState(Parcelable p0){} + public void onRtlPropertiesChanged(int p0){} + public void onScreenStateChanged(int p0){} + public void onWindowFocusChanged(boolean p0){} + public void removeTextChangedListener(TextWatcher p0){} + public void sendAccessibilityEventUnchecked(AccessibilityEvent p0){} + public void setAllCaps(boolean p0){} + public void setAutoSizeTextTypeUniformWithConfiguration(int p0, int p1, int p2, int p3){} + public void setAutoSizeTextTypeUniformWithPresetSizes(int[] p0, int p1){} + public void setAutoSizeTextTypeWithDefaults(int p0){} + public void setBreakStrategy(int p0){} + public void setCompoundDrawablePadding(int p0){} + public void setCompoundDrawableTintBlendMode(BlendMode p0){} + public void setCompoundDrawableTintList(ColorStateList p0){} + public void setCompoundDrawableTintMode(PorterDuff.Mode p0){} + public void setCompoundDrawables(Drawable p0, Drawable p1, Drawable p2, Drawable p3){} + public void setCompoundDrawablesRelative(Drawable p0, Drawable p1, Drawable p2, Drawable p3){} + public void setCompoundDrawablesRelativeWithIntrinsicBounds(Drawable p0, Drawable p1, Drawable p2, Drawable p3){} + public void setCompoundDrawablesRelativeWithIntrinsicBounds(int p0, int p1, int p2, int p3){} + public void setCompoundDrawablesWithIntrinsicBounds(Drawable p0, Drawable p1, Drawable p2, Drawable p3){} + public void setCompoundDrawablesWithIntrinsicBounds(int p0, int p1, int p2, int p3){} + public void setCursorVisible(boolean p0){} + public void setCustomInsertionActionModeCallback(ActionMode.Callback p0){} + public void setCustomSelectionActionModeCallback(ActionMode.Callback p0){} + public void setElegantTextHeight(boolean p0){} + public void setEllipsize(TextUtils.TruncateAt p0){} + public void setEms(int p0){} + public void setEnabled(boolean p0){} + public void setError(CharSequence p0){} + public void setError(CharSequence p0, Drawable p1){} + public void setExtractedText(ExtractedText p0){} + public void setFallbackLineSpacing(boolean p0){} + public void setFilters(InputFilter[] p0){} + public void setFirstBaselineToTopHeight(int p0){} + public void setFontFeatureSettings(String p0){} + public void setFreezesText(boolean p0){} + public void setGravity(int p0){} + public void setHeight(int p0){} + public void setHighlightColor(int p0){} + public void setHorizontallyScrolling(boolean p0){} + public void setHyphenationFrequency(int p0){} + public void setImeActionLabel(CharSequence p0, int p1){} + public void setImeHintLocales(LocaleList p0){} + public void setImeOptions(int p0){} + public void setIncludeFontPadding(boolean p0){} + public void setInputExtras(int p0){} + public void setInputType(int p0){} + public void setJustificationMode(int p0){} + public void setKeyListener(KeyListener p0){} + public void setLastBaselineToBottomHeight(int p0){} + public void setLetterSpacing(float p0){} + public void setLineHeight(int p0){} + public void setLineSpacing(float p0, float p1){} + public void setLines(int p0){} + public void setMarqueeRepeatLimit(int p0){} + public void setMaxEms(int p0){} + public void setMaxHeight(int p0){} + public void setMaxLines(int p0){} + public void setMaxWidth(int p0){} + public void setMinEms(int p0){} + public void setMinHeight(int p0){} + public void setMinLines(int p0){} + public void setMinWidth(int p0){} + public void setOnEditorActionListener(TextView.OnEditorActionListener p0){} + public void setPadding(int p0, int p1, int p2, int p3){} + public void setPaddingRelative(int p0, int p1, int p2, int p3){} + public void setPaintFlags(int p0){} + public void setPrivateImeOptions(String p0){} + public void setRawInputType(int p0){} + public void setScroller(Scroller p0){} + public void setSelectAllOnFocus(boolean p0){} + public void setSelected(boolean p0){} + public void setShadowLayer(float p0, float p1, float p2, int p3){} + public void setSingleLine(){} + public void setSingleLine(boolean p0){} + public void setText(CharSequence p0, TextView.BufferType p1){} + public void setTextAppearance(Context p0, int p1){} + public void setTextAppearance(int p0){} + public void setTextClassifier(TextClassifier p0){} + public void setTextColor(ColorStateList p0){} + public void setTextColor(int p0){} + public void setTextCursorDrawable(Drawable p0){} + public void setTextCursorDrawable(int p0){} + public void setTextIsSelectable(boolean p0){} + public void setTextLocale(Locale p0){} + public void setTextLocales(LocaleList p0){} + public void setTextMetricsParams(PrecomputedText.Params p0){} + public void setTextScaleX(float p0){} + public void setTextSelectHandle(Drawable p0){} + public void setTextSelectHandle(int p0){} + public void setTextSelectHandleLeft(Drawable p0){} + public void setTextSelectHandleLeft(int p0){} + public void setTextSelectHandleRight(Drawable p0){} + public void setTextSelectHandleRight(int p0){} + public void setTextSize(float p0){} + public void setTextSize(int p0, float p1){} + public void setTypeface(Typeface p0){} + public void setTypeface(Typeface p0, int p1){} + public void setWidth(int p0){} + static public enum BufferType + { + EDITABLE, NORMAL, SPANNABLE; + private BufferType() {} + } + static public interface OnEditorActionListener + { + boolean onEditorAction(TextView p0, int p1, KeyEvent p2); + } } diff --git a/java/ql/test/stubs/google-android-9.0.0/android/widget/Toolbar.java b/java/ql/test/stubs/google-android-9.0.0/android/widget/Toolbar.java new file mode 100644 index 00000000000..91b6be9e53d --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/widget/Toolbar.java @@ -0,0 +1,116 @@ +// Generated automatically from android.widget.Toolbar for testing purposes + +package android.widget; + +import android.app.ActionBar; +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.os.Parcelable; +import android.util.AttributeSet; +import android.view.Menu; +import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; + +public class Toolbar extends ViewGroup +{ + protected Toolbar() {} + protected Parcelable onSaveInstanceState(){ return null; } + protected Toolbar.LayoutParams generateDefaultLayoutParams(){ return null; } + protected Toolbar.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p0){ return null; } + protected boolean checkLayoutParams(ViewGroup.LayoutParams p0){ return false; } + protected void onAttachedToWindow(){} + protected void onDetachedFromWindow(){} + protected void onLayout(boolean p0, int p1, int p2, int p3, int p4){} + protected void onMeasure(int p0, int p1){} + protected void onRestoreInstanceState(Parcelable p0){} + public CharSequence getCollapseContentDescription(){ return null; } + public CharSequence getLogoDescription(){ return null; } + public CharSequence getNavigationContentDescription(){ return null; } + public CharSequence getSubtitle(){ return null; } + public CharSequence getTitle(){ return null; } + public Drawable getCollapseIcon(){ return null; } + public Drawable getLogo(){ return null; } + public Drawable getNavigationIcon(){ return null; } + public Drawable getOverflowIcon(){ return null; } + public Menu getMenu(){ return null; } + public Toolbar(Context p0){} + public Toolbar(Context p0, AttributeSet p1){} + public Toolbar(Context p0, AttributeSet p1, int p2){} + public Toolbar(Context p0, AttributeSet p1, int p2, int p3){} + public Toolbar.LayoutParams generateLayoutParams(AttributeSet p0){ return null; } + public boolean hasExpandedActionView(){ return false; } + public boolean hideOverflowMenu(){ return false; } + public boolean isOverflowMenuShowing(){ return false; } + public boolean onTouchEvent(MotionEvent p0){ return false; } + public boolean showOverflowMenu(){ return false; } + public int getContentInsetEnd(){ return 0; } + public int getContentInsetEndWithActions(){ return 0; } + public int getContentInsetLeft(){ return 0; } + public int getContentInsetRight(){ return 0; } + public int getContentInsetStart(){ return 0; } + public int getContentInsetStartWithNavigation(){ return 0; } + public int getCurrentContentInsetEnd(){ return 0; } + public int getCurrentContentInsetLeft(){ return 0; } + public int getCurrentContentInsetRight(){ return 0; } + public int getCurrentContentInsetStart(){ return 0; } + public int getPopupTheme(){ return 0; } + public int getTitleMarginBottom(){ return 0; } + public int getTitleMarginEnd(){ return 0; } + public int getTitleMarginStart(){ return 0; } + public int getTitleMarginTop(){ return 0; } + public void collapseActionView(){} + public void dismissPopupMenus(){} + public void inflateMenu(int p0){} + public void onRtlPropertiesChanged(int p0){} + public void setCollapseContentDescription(CharSequence p0){} + public void setCollapseContentDescription(int p0){} + public void setCollapseIcon(Drawable p0){} + public void setCollapseIcon(int p0){} + public void setContentInsetEndWithActions(int p0){} + public void setContentInsetStartWithNavigation(int p0){} + public void setContentInsetsAbsolute(int p0, int p1){} + public void setContentInsetsRelative(int p0, int p1){} + public void setLogo(Drawable p0){} + public void setLogo(int p0){} + public void setLogoDescription(CharSequence p0){} + public void setLogoDescription(int p0){} + public void setNavigationContentDescription(CharSequence p0){} + public void setNavigationContentDescription(int p0){} + public void setNavigationIcon(Drawable p0){} + public void setNavigationIcon(int p0){} + public void setNavigationOnClickListener(View.OnClickListener p0){} + public void setOnMenuItemClickListener(Toolbar.OnMenuItemClickListener p0){} + public void setOverflowIcon(Drawable p0){} + public void setPopupTheme(int p0){} + public void setSubtitle(CharSequence p0){} + public void setSubtitle(int p0){} + public void setSubtitleTextAppearance(Context p0, int p1){} + public void setSubtitleTextColor(int p0){} + public void setTitle(CharSequence p0){} + public void setTitle(int p0){} + public void setTitleMargin(int p0, int p1, int p2, int p3){} + public void setTitleMarginBottom(int p0){} + public void setTitleMarginEnd(int p0){} + public void setTitleMarginStart(int p0){} + public void setTitleMarginTop(int p0){} + public void setTitleTextAppearance(Context p0, int p1){} + public void setTitleTextColor(int p0){} + static public class LayoutParams extends ActionBar.LayoutParams + { + protected LayoutParams() {} + public LayoutParams(ActionBar.LayoutParams p0){} + public LayoutParams(Context p0, AttributeSet p1){} + public LayoutParams(Toolbar.LayoutParams p0){} + public LayoutParams(ViewGroup.LayoutParams p0){} + public LayoutParams(ViewGroup.MarginLayoutParams p0){} + public LayoutParams(int p0){} + public LayoutParams(int p0, int p1){} + public LayoutParams(int p0, int p1, int p2){} + } + static public interface OnMenuItemClickListener + { + boolean onMenuItemClick(MenuItem p0); + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/window/SplashScreen.java b/java/ql/test/stubs/google-android-9.0.0/android/window/SplashScreen.java new file mode 100644 index 00000000000..1c927807b45 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/window/SplashScreen.java @@ -0,0 +1,16 @@ +// Generated automatically from android.window.SplashScreen for testing purposes + +package android.window; + +import android.window.SplashScreenView; + +public interface SplashScreen +{ + static public interface OnExitAnimationListener + { + void onSplashScreenExit(SplashScreenView p0); + } + void clearOnExitAnimationListener(); + void setOnExitAnimationListener(SplashScreen.OnExitAnimationListener p0); + void setSplashScreenTheme(int p0); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/window/SplashScreenView.java b/java/ql/test/stubs/google-android-9.0.0/android/window/SplashScreenView.java new file mode 100644 index 00000000000..01ad6a2cd0b --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/window/SplashScreenView.java @@ -0,0 +1,18 @@ +// Generated automatically from android.window.SplashScreenView for testing purposes + +package android.window; + +import android.view.View; +import android.widget.FrameLayout; +import java.time.Duration; +import java.time.Instant; + +public class SplashScreenView extends FrameLayout +{ + protected void onDetachedFromWindow(){} + public Duration getIconAnimationDuration(){ return null; } + public Instant getIconAnimationStart(){ return null; } + public View getIconView(){ return null; } + public void remove(){} + public void setAlpha(float p0){} +} From a2245bb85856863e108aa077992a289476d281fa Mon Sep 17 00:00:00 2001 From: Joe Farebrother Date: Tue, 21 Jun 2022 16:15:24 +0100 Subject: [PATCH 469/505] Fix test --- .../security/AndroidWebViewCertificateValidationQuery.qll | 4 +++- .../CWE-295/ImproperWebVeiwCertificateValidation/Test.java | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/java/ql/lib/semmle/code/java/security/AndroidWebViewCertificateValidationQuery.qll b/java/ql/lib/semmle/code/java/security/AndroidWebViewCertificateValidationQuery.qll index 5b459f07fac..609cafe4eb9 100644 --- a/java/ql/lib/semmle/code/java/security/AndroidWebViewCertificateValidationQuery.qll +++ b/java/ql/lib/semmle/code/java/security/AndroidWebViewCertificateValidationQuery.qll @@ -2,7 +2,9 @@ import java class OnReceivedSslErrorMethod extends Method { OnReceivedSslErrorMethod() { - this.hasQualifiedName("android.webkit", "WebViewClient", "onReceivedSslError") + this.overrides*(any(Method m | + m.hasQualifiedName("android.webkit", "WebViewClient", "onReceivedSslError") + )) } Parameter handlerArg() { result = this.getParameter(1) } diff --git a/java/ql/test/query-tests/security/CWE-295/ImproperWebVeiwCertificateValidation/Test.java b/java/ql/test/query-tests/security/CWE-295/ImproperWebVeiwCertificateValidation/Test.java index 3ddb0c6aad5..180989590f0 100644 --- a/java/ql/test/query-tests/security/CWE-295/ImproperWebVeiwCertificateValidation/Test.java +++ b/java/ql/test/query-tests/security/CWE-295/ImproperWebVeiwCertificateValidation/Test.java @@ -9,8 +9,8 @@ import android.app.Activity; class Test { class A extends WebViewClient { - public void onReceivedSslError (WebView view, SslErrorHandler handler, SslError error) { - handler.proceed(); // $hasResult + public void onReceivedSslError (WebView view, SslErrorHandler handler, SslError error) { // $hasResult + handler.proceed(); } } From f8ccbcba708c7f4d0a759b9b25eb5e3209aa7b60 Mon Sep 17 00:00:00 2001 From: Joe Farebrother Date: Wed, 22 Jun 2022 14:01:52 +0100 Subject: [PATCH 470/505] Add qhelp --- ...ImproperWebViewCertifiacteValidation.qhelp | 43 +++++++++++++++++++ .../ImproperWebViewCertificateValidation.java | 22 ++++++++++ 2 files changed, 65 insertions(+) create mode 100644 java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertifiacteValidation.qhelp create mode 100644 java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.java diff --git a/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertifiacteValidation.qhelp b/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertifiacteValidation.qhelp new file mode 100644 index 00000000000..e00108ea51e --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertifiacteValidation.qhelp @@ -0,0 +1,43 @@ + + + +

    +If the onReceivedSslError method of an Android WebViewClient always calls proceed on the given SslErrorHandler, it trusts any certificate. +This allows an attacker to perform a machine-in-the-middle attack against the application, therefore breaking any security Transport Layer Security (TLS) gives. +

    + +

    +An attack might look like this: +

    + +
      +
    1. The vulnerable application connects to https://example.com.
    2. +
    3. The attacker intercepts this connection and presents a valid, self-signed certificate for https://example.com.
    4. +
    5. The vulnerable application calls the onReceivedSslError method to check whether it should trust the certificate.
    6. +
    7. The onReceivedSslError method of your WebViewClient calls SslErrorHandler.proceed.
    8. +
    9. The vulnerable application accepts the certificate and proceeds with the connection since your WevViewClient trusted it by proceeding.
    10. +
    11. The attacker can now read the data your application sends to https://example.com and/or alter its replies while the application thinks the connection is secure.
    12. +
    + + + +

    +Do not use a call SslerrorHandler.proceed unconditonally. +If you have to use a self-signed certificate, only accept that certificate, not all certificates. +

    + +
    + + +

    +In the first (bad) example, the WebViewClient trusts all certificates by always calling SslErrorHandler.proceed. +In the second (good) example, only certificates signed by a certain public key are accepted. +

    + +
    + + + +
    diff --git a/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.java b/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.java new file mode 100644 index 00000000000..358800c5746 --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.java @@ -0,0 +1,22 @@ +class Bad extends WebViewClient { + // BAD: All certificates are trusted. + public void onReceivedSslError (WebView view, SslErrorHandler handler, SslError error) { // $hasResult + handler.proceed(); + } +} + +class Good extends WebViewClient { + PublicKey myPubKey = ...; + + // GOOD: Only certificates signed by a certain public key are trusted. + public void onReceivedSslError (WebView view, SslErrorHandler handler, SslError error) { // $hasResult + try { + X509Certificate cert = error.getCertificate().getX509Certificate(); + cert.verify(this.myPubKey); + handler.proceed(); + } + catch (CertificateException|NoSuchAlgorithmException|InvalidKeyException|NoSuchProviderException|SignatureException e) { + handler.cancel(); + } + } +} \ No newline at end of file From 0d09484efcbe1ffe78c8b9994dba9ff5cbdf19f9 Mon Sep 17 00:00:00 2001 From: Joe Farebrother Date: Wed, 22 Jun 2022 14:08:35 +0100 Subject: [PATCH 471/505] Add change note --- .../2022-06-22-improper-webview-certificate-validation.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 java/ql/src/change-notes/2022-06-22-improper-webview-certificate-validation.md diff --git a/java/ql/src/change-notes/2022-06-22-improper-webview-certificate-validation.md b/java/ql/src/change-notes/2022-06-22-improper-webview-certificate-validation.md new file mode 100644 index 00000000000..87ab835f547 --- /dev/null +++ b/java/ql/src/change-notes/2022-06-22-improper-webview-certificate-validation.md @@ -0,0 +1,4 @@ +--- +category: newQuery +--- +* A new query "Android `WebVeiw` that accepts all certificates" (`java/improper-webview-certificate-validation`) has been added. This query finds implementations of `WebViewClient`s that accept all certificates in the case of an SSL error. \ No newline at end of file From 03c2a0e8187e28762af0721e26dd403778edb5c9 Mon Sep 17 00:00:00 2001 From: Joe Farebrother Date: Wed, 22 Jun 2022 14:26:49 +0100 Subject: [PATCH 472/505] Add missing qldoc --- .../security/AndroidWebViewCertificateValidationQuery.qll | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/java/ql/lib/semmle/code/java/security/AndroidWebViewCertificateValidationQuery.qll b/java/ql/lib/semmle/code/java/security/AndroidWebViewCertificateValidationQuery.qll index 609cafe4eb9..af8f6eeb4a8 100644 --- a/java/ql/lib/semmle/code/java/security/AndroidWebViewCertificateValidationQuery.qll +++ b/java/ql/lib/semmle/code/java/security/AndroidWebViewCertificateValidationQuery.qll @@ -1,5 +1,8 @@ +/** Defintions for the web view certificate validation query */ + import java +/** A method that overrides `WebViewClient.onReceivedSslError` */ class OnReceivedSslErrorMethod extends Method { OnReceivedSslErrorMethod() { this.overrides*(any(Method m | @@ -7,21 +10,25 @@ class OnReceivedSslErrorMethod extends Method { )) } + /** Gets the `SslErrorHandler` argument to this method. */ Parameter handlerArg() { result = this.getParameter(1) } } +/** A call to `SslErrorHandler.cancel` */ private class SslCancelCall extends MethodAccess { SslCancelCall() { this.getMethod().hasQualifiedName("android.webkit", "SslErrorHandler", "cancel") } } +/** A call to `SslErrorHandler.proceed` */ private class SslProceedCall extends MethodAccess { SslProceedCall() { this.getMethod().hasQualifiedName("android.webkit", "SslErrorHandler", "proceed") } } +/** Holds if `m` trusts all certifiates by calling `SslErrorHandler.proceed` unconditionally. */ predicate trustsAllCerts(OnReceivedSslErrorMethod m) { exists(SslProceedCall pr | pr.getQualifier().(VarAccess).getVariable() = m.handlerArg()) and not exists(SslCancelCall ca | ca.getQualifier().(VarAccess).getVariable() = m.handlerArg()) From abf894a64cb7f4c2f2ca0fee372bc7de0a76d786 Mon Sep 17 00:00:00 2001 From: Joe Farebrother Date: Wed, 29 Jun 2022 13:57:51 +0100 Subject: [PATCH 473/505] Fix typos --- .../security/AndroidWebViewCertificateValidationQuery.qll | 4 ++-- ...ation.qhelp => ImproperWebViewCertificateValidation.qhelp} | 2 +- .../CWE/CWE-295/ImproperWebViewCertificateValidation.ql | 2 +- .../2022-06-22-improper-webview-certificate-validation.md | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) rename java/ql/src/Security/CWE/CWE-295/{ImproperWebViewCertifiacteValidation.qhelp => ImproperWebViewCertificateValidation.qhelp} (96%) diff --git a/java/ql/lib/semmle/code/java/security/AndroidWebViewCertificateValidationQuery.qll b/java/ql/lib/semmle/code/java/security/AndroidWebViewCertificateValidationQuery.qll index af8f6eeb4a8..e8eae5b96f9 100644 --- a/java/ql/lib/semmle/code/java/security/AndroidWebViewCertificateValidationQuery.qll +++ b/java/ql/lib/semmle/code/java/security/AndroidWebViewCertificateValidationQuery.qll @@ -1,4 +1,4 @@ -/** Defintions for the web view certificate validation query */ +/** Definitions for the web view certificate validation query */ import java @@ -28,7 +28,7 @@ private class SslProceedCall extends MethodAccess { } } -/** Holds if `m` trusts all certifiates by calling `SslErrorHandler.proceed` unconditionally. */ +/** Holds if `m` trusts all certificates by calling `SslErrorHandler.proceed` unconditionally. */ predicate trustsAllCerts(OnReceivedSslErrorMethod m) { exists(SslProceedCall pr | pr.getQualifier().(VarAccess).getVariable() = m.handlerArg()) and not exists(SslCancelCall ca | ca.getQualifier().(VarAccess).getVariable() = m.handlerArg()) diff --git a/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertifiacteValidation.qhelp b/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.qhelp similarity index 96% rename from java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertifiacteValidation.qhelp rename to java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.qhelp index e00108ea51e..8fb725ff082 100644 --- a/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertifiacteValidation.qhelp +++ b/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.qhelp @@ -24,7 +24,7 @@ An attack might look like this:

    -Do not use a call SslerrorHandler.proceed unconditonally. +Do not use a call SslerrorHandler.proceed unconditionally. If you have to use a self-signed certificate, only accept that certificate, not all certificates.

    diff --git a/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.ql b/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.ql index 5d2f4cf7eb9..aac3a99be4c 100644 --- a/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.ql +++ b/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.ql @@ -1,5 +1,5 @@ /** - * @name Android `WebVeiw` that accepts all certificates + * @name Android `WebView` that accepts all certificates * @description Trusting all certificates allows an attacker to perform a machine-in-the-middle attack. * @kind problem * @problem.severity error diff --git a/java/ql/src/change-notes/2022-06-22-improper-webview-certificate-validation.md b/java/ql/src/change-notes/2022-06-22-improper-webview-certificate-validation.md index 87ab835f547..3e80487d772 100644 --- a/java/ql/src/change-notes/2022-06-22-improper-webview-certificate-validation.md +++ b/java/ql/src/change-notes/2022-06-22-improper-webview-certificate-validation.md @@ -1,4 +1,4 @@ --- category: newQuery --- -* A new query "Android `WebVeiw` that accepts all certificates" (`java/improper-webview-certificate-validation`) has been added. This query finds implementations of `WebViewClient`s that accept all certificates in the case of an SSL error. \ No newline at end of file +* A new query "Android `WebView` that accepts all certificates" (`java/improper-webview-certificate-validation`) has been added. This query finds implementations of `WebViewClient`s that accept all certificates in the case of an SSL error. \ No newline at end of file From 04df556861676d101ba345c147f969f75419dd4d Mon Sep 17 00:00:00 2001 From: Joe Farebrother Date: Tue, 19 Jul 2022 14:08:28 +0100 Subject: [PATCH 474/505] Add suggested reference --- .../CWE/CWE-295/ImproperWebViewCertificateValidation.qhelp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.qhelp b/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.qhelp index 8fb725ff082..2a8d3b58a4c 100644 --- a/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.qhelp +++ b/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.qhelp @@ -39,5 +39,8 @@ In the second (good) example, only certificates signed by a certain public key a
    +
  • +WebViewClient.onReceivedSslError documentation. +
  • From 79b1f24133f8c74af5be9ba9a43d258934210463 Mon Sep 17 00:00:00 2001 From: Joe Farebrother Date: Fri, 22 Jul 2022 11:11:43 +0100 Subject: [PATCH 475/505] Change machine-in-the-middle to man-in-the-middle --- .../CWE/CWE-295/ImproperWebViewCertificateValidation.qhelp | 2 +- .../CWE/CWE-295/ImproperWebViewCertificateValidation.ql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.qhelp b/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.qhelp index 2a8d3b58a4c..41429f4996f 100644 --- a/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.qhelp +++ b/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.qhelp @@ -5,7 +5,7 @@

    If the onReceivedSslError method of an Android WebViewClient always calls proceed on the given SslErrorHandler, it trusts any certificate. -This allows an attacker to perform a machine-in-the-middle attack against the application, therefore breaking any security Transport Layer Security (TLS) gives. +This allows an attacker to perform a man-in-the-middle attack against the application, therefore breaking any security Transport Layer Security (TLS) gives.

    diff --git a/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.ql b/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.ql index aac3a99be4c..4838621311b 100644 --- a/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.ql +++ b/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.ql @@ -1,6 +1,6 @@ /** * @name Android `WebView` that accepts all certificates - * @description Trusting all certificates allows an attacker to perform a machine-in-the-middle attack. + * @description Trusting all certificates allows an attacker to perform a man-in-the-middle attack. * @kind problem * @problem.severity error * @security-severity 7.5 From e9f9e681efaa639361c2b5340e2a3a0336d8eb37 Mon Sep 17 00:00:00 2001 From: Joe Farebrother Date: Fri, 22 Jul 2022 11:24:32 +0100 Subject: [PATCH 476/505] Change man-in-the-middle back to machine-in-the-middle (gender-neutral language) This reverts commit d5ab330450d3f5c1d36d0d9b6a8f1dc32bc908e3. --- .../CWE/CWE-295/ImproperWebViewCertificateValidation.qhelp | 2 +- .../CWE/CWE-295/ImproperWebViewCertificateValidation.ql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.qhelp b/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.qhelp index 41429f4996f..2a8d3b58a4c 100644 --- a/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.qhelp +++ b/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.qhelp @@ -5,7 +5,7 @@

    If the onReceivedSslError method of an Android WebViewClient always calls proceed on the given SslErrorHandler, it trusts any certificate. -This allows an attacker to perform a man-in-the-middle attack against the application, therefore breaking any security Transport Layer Security (TLS) gives. +This allows an attacker to perform a machine-in-the-middle attack against the application, therefore breaking any security Transport Layer Security (TLS) gives.

    diff --git a/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.ql b/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.ql index 4838621311b..aac3a99be4c 100644 --- a/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.ql +++ b/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.ql @@ -1,6 +1,6 @@ /** * @name Android `WebView` that accepts all certificates - * @description Trusting all certificates allows an attacker to perform a man-in-the-middle attack. + * @description Trusting all certificates allows an attacker to perform a machine-in-the-middle attack. * @kind problem * @problem.severity error * @security-severity 7.5 From dd83c17144fc339606f629fbc8bf11c15679c9a5 Mon Sep 17 00:00:00 2001 From: Joe Farebrother Date: Tue, 26 Jul 2022 16:29:49 +0100 Subject: [PATCH 477/505] Use more precise control flow logic --- .../AndroidWebViewCertificateValidationQuery.qll | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/java/ql/lib/semmle/code/java/security/AndroidWebViewCertificateValidationQuery.qll b/java/ql/lib/semmle/code/java/security/AndroidWebViewCertificateValidationQuery.qll index e8eae5b96f9..3d47f77a2bb 100644 --- a/java/ql/lib/semmle/code/java/security/AndroidWebViewCertificateValidationQuery.qll +++ b/java/ql/lib/semmle/code/java/security/AndroidWebViewCertificateValidationQuery.qll @@ -14,13 +14,6 @@ class OnReceivedSslErrorMethod extends Method { Parameter handlerArg() { result = this.getParameter(1) } } -/** A call to `SslErrorHandler.cancel` */ -private class SslCancelCall extends MethodAccess { - SslCancelCall() { - this.getMethod().hasQualifiedName("android.webkit", "SslErrorHandler", "cancel") - } -} - /** A call to `SslErrorHandler.proceed` */ private class SslProceedCall extends MethodAccess { SslProceedCall() { @@ -30,6 +23,7 @@ private class SslProceedCall extends MethodAccess { /** Holds if `m` trusts all certificates by calling `SslErrorHandler.proceed` unconditionally. */ predicate trustsAllCerts(OnReceivedSslErrorMethod m) { - exists(SslProceedCall pr | pr.getQualifier().(VarAccess).getVariable() = m.handlerArg()) and - not exists(SslCancelCall ca | ca.getQualifier().(VarAccess).getVariable() = m.handlerArg()) + exists(SslProceedCall pr | pr.getQualifier().(VarAccess).getVariable() = m.handlerArg() | + pr.getBasicBlock().bbPostDominates(m.getBody().getBasicBlock()) + ) } From 400071091c9d66f02ec7c39461f9a696240326e0 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 5 Aug 2022 13:45:25 +0200 Subject: [PATCH 478/505] C#: Also disable shared compilation in the tracer for `dotnet msbuild` --- csharp/tools/tracing-config.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/csharp/tools/tracing-config.lua b/csharp/tools/tracing-config.lua index 8aafc63e7e5..efa936b41dd 100644 --- a/csharp/tools/tracing-config.lua +++ b/csharp/tools/tracing-config.lua @@ -31,7 +31,7 @@ function RegisterExtractorPack(id) local firstCharacter = string.sub(arg, 1, 1) if not (firstCharacter == '-') and not (firstCharacter == '/') then Log(1, 'Dotnet subcommand detected: %s', arg) - if arg == 'build' then match = true end + if arg == 'build' or arg == 'msbuild' then match = true end break end end From 6cfeb24d940b9f2a8aff2fd9c06479df235802e2 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 5 Aug 2022 13:30:45 +0100 Subject: [PATCH 479/505] Swift: More comments. --- .../swift/controlflow/internal/ControlFlowGraphImpl.qll | 6 +++++- .../swift/dataflow/internal/TaintTrackingPrivate.qll | 8 ++++++++ swift/ql/lib/codeql/swift/elements/expr/TapExpr.qll | 9 ++++++++- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll b/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll index 6549d9692f2..b367aa80d52 100644 --- a/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll +++ b/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll @@ -1349,17 +1349,21 @@ module Exprs { } } + /** Control-flow for a `TapExpr`. See the QLDoc for `TapExpr` for the semantics of a `TapExpr`. */ private class TapExprTree extends AstStandardPostOrderTree { override TapExpr ast; final override ControlFlowElement getChildElement(int i) { + // We first visit the local variable declaration. i = 0 and result.asAstNode() = ast.getVar() or + // Then we visit the expression that gives the local variable its initial value. i = 1 and result.asAstNode() = ast.getSubExpr().getFullyConverted() or - // Note: The CFG for the body will skip the first element in the + // And finally, we visit the body that potentially mutates the local variable. + // Note that the CFG for the body will skip the first element in the // body because it's guarenteed to be the variable declaration // that we've already visited at i = 0. See the explanation // in `BraceStmtTree` for why this is necessary. diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/TaintTrackingPrivate.qll b/swift/ql/lib/codeql/swift/dataflow/internal/TaintTrackingPrivate.qll index 804bada88bf..58e29b61bba 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/TaintTrackingPrivate.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/TaintTrackingPrivate.qll @@ -20,6 +20,14 @@ private module Cached { cached predicate defaultAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { // Flow through one argument of `appendLiteral` and `appendInterpolation` and to the second argument. + // This is needed for string interpolation generated by the compiler. An interpolated string + // like `"I am \(n) years old."` is represented as + // ``` + // $interpolated = "" + // appendLiteral(&$interpolated, "I am ") + // appendInterpolation(&$interpolated, n) + // appendLiteral(&$interpolated, " years old.") + // ``` exists(ApplyExpr apply1, ApplyExpr apply2, ExprCfgNode e | nodeFrom.asExpr() = [apply1, apply2].getAnArgument().getExpr() and apply1.getFunction() = apply2 and diff --git a/swift/ql/lib/codeql/swift/elements/expr/TapExpr.qll b/swift/ql/lib/codeql/swift/elements/expr/TapExpr.qll index e0608415f1d..e200b613823 100644 --- a/swift/ql/lib/codeql/swift/elements/expr/TapExpr.qll +++ b/swift/ql/lib/codeql/swift/elements/expr/TapExpr.qll @@ -1,4 +1,11 @@ -// generated by codegen/codegen.py, remove this comment if you wish to edit this file private import codeql.swift.generated.expr.TapExpr +/** + * A `TapExpr` is an internal expression generated by the Swift compiler. + * + * If `e` is a `TapExpr`, the semantics of evaluating `e` is: + * 1. Create a local variable `e.getVar()` and assign it the value `e.getSubExpr()`. + * 2. Execute `e.getBody()` which potentially modifies the local variable. + * 3. Return the value of the local variable. + */ class TapExpr extends TapExprBase { } From 03b854a1edacd5373e563c31bbe14eedfd633efb Mon Sep 17 00:00:00 2001 From: Tony Torralba Date: Fri, 5 Aug 2022 15:29:17 +0200 Subject: [PATCH 480/505] Add test for initializer method --- .../library-tests/frameworks/android/asynctask/Test.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/java/ql/test/library-tests/frameworks/android/asynctask/Test.java b/java/ql/test/library-tests/frameworks/android/asynctask/Test.java index b471b172c64..6f0bc1e84fa 100644 --- a/java/ql/test/library-tests/frameworks/android/asynctask/Test.java +++ b/java/ql/test/library-tests/frameworks/android/asynctask/Test.java @@ -35,9 +35,13 @@ public class Test { } } - class TestConstructorTask extends AsyncTask { + static class TestConstructorTask extends AsyncTask { private Object field; private Object safeField; + private Object initField; + { + initField = Test.source("init"); + } public TestConstructorTask(Object field, Object safeField) { this.field = field; @@ -49,6 +53,7 @@ public class Test { sink(params[0]); // $ hasTaintFlow=params sink(field); // $ hasValueFlow=constructor sink(safeField); // Safe + sink(initField); // $ hasValueFlow=init return params[0]; } @@ -57,6 +62,7 @@ public class Test { sink(param); // $ hasTaintFlow=params sink(field); // $ hasValueFlow=constructor sink(safeField); // Safe + sink(initField); // $ hasValueFlow=init } } From 98b930cd6750dd7d7deddf13d74be97f5ed92e6d Mon Sep 17 00:00:00 2001 From: Tony Torralba Date: Mon, 8 Aug 2022 09:23:12 +0200 Subject: [PATCH 481/505] Accept test changes in experimental query after AsyncTask improvements --- .../security/CWE-200/SensitiveAndroidFileLeak.expected | 7 ------- 1 file changed, 7 deletions(-) diff --git a/java/ql/test/experimental/query-tests/security/CWE-200/SensitiveAndroidFileLeak.expected b/java/ql/test/experimental/query-tests/security/CWE-200/SensitiveAndroidFileLeak.expected index d931ead82ac..b67c634ad9f 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-200/SensitiveAndroidFileLeak.expected +++ b/java/ql/test/experimental/query-tests/security/CWE-200/SensitiveAndroidFileLeak.expected @@ -4,17 +4,13 @@ edges | FileService.java:21:28:21:64 | getStringExtra(...) : Object | FileService.java:25:42:25:50 | localPath : Object | | FileService.java:25:13:25:51 | makeParamsToExecute(...) : Object[] | FileService.java:40:41:40:55 | params : Object[] | | FileService.java:25:13:25:51 | makeParamsToExecute(...) [[]] : Object | FileService.java:25:13:25:51 | makeParamsToExecute(...) : Object[] | -| FileService.java:25:13:25:51 | makeParamsToExecute(...) [[]] : Object | FileService.java:40:41:40:55 | params [[]] : Object | | FileService.java:25:42:25:50 | localPath : Object | FileService.java:25:13:25:51 | makeParamsToExecute(...) [[]] : Object | | FileService.java:25:42:25:50 | localPath : Object | FileService.java:32:13:32:28 | sourceUri : Object | | FileService.java:32:13:32:28 | sourceUri : Object | FileService.java:35:17:35:25 | sourceUri : Object | | FileService.java:34:20:36:13 | {...} [[]] : Object | FileService.java:34:20:36:13 | new Object[] [[]] : Object | | FileService.java:35:17:35:25 | sourceUri : Object | FileService.java:34:20:36:13 | {...} [[]] : Object | | FileService.java:40:41:40:55 | params : Object[] | FileService.java:44:33:44:52 | (...)... : Object | -| FileService.java:40:41:40:55 | params [[]] : Object | FileService.java:44:44:44:49 | params [[]] : Object | | FileService.java:44:33:44:52 | (...)... : Object | FileService.java:45:53:45:59 | ...[...] | -| FileService.java:44:44:44:49 | params [[]] : Object | FileService.java:44:44:44:52 | ...[...] : Object | -| FileService.java:44:44:44:52 | ...[...] : Object | FileService.java:44:33:44:52 | (...)... : Object | | LeakFileActivity2.java:15:13:15:18 | intent : Intent | LeakFileActivity2.java:16:26:16:31 | intent : Intent | | LeakFileActivity2.java:16:26:16:31 | intent : Intent | FileService.java:20:31:20:43 | intent : Intent | | LeakFileActivity.java:14:35:14:38 | data : Intent | LeakFileActivity.java:18:40:18:59 | contentIntent : Intent | @@ -34,10 +30,7 @@ nodes | FileService.java:34:20:36:13 | {...} [[]] : Object | semmle.label | {...} [[]] : Object | | FileService.java:35:17:35:25 | sourceUri : Object | semmle.label | sourceUri : Object | | FileService.java:40:41:40:55 | params : Object[] | semmle.label | params : Object[] | -| FileService.java:40:41:40:55 | params [[]] : Object | semmle.label | params [[]] : Object | | FileService.java:44:33:44:52 | (...)... : Object | semmle.label | (...)... : Object | -| FileService.java:44:44:44:49 | params [[]] : Object | semmle.label | params [[]] : Object | -| FileService.java:44:44:44:52 | ...[...] : Object | semmle.label | ...[...] : Object | | FileService.java:45:53:45:59 | ...[...] | semmle.label | ...[...] | | LeakFileActivity2.java:15:13:15:18 | intent : Intent | semmle.label | intent : Intent | | LeakFileActivity2.java:16:26:16:31 | intent : Intent | semmle.label | intent : Intent | From d16a154f9e4941839fa99b43a040a98ccbf14203 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 8 Aug 2022 10:45:55 +0200 Subject: [PATCH 482/505] Address review comment --- .../consistency-queries/DataFlowConsistency.ql | 2 +- .../ruby/dataflow/internal/DataFlowPrivate.qll | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ruby/ql/consistency-queries/DataFlowConsistency.ql b/ruby/ql/consistency-queries/DataFlowConsistency.ql index 1a891dd40f7..80eb90913d0 100644 --- a/ruby/ql/consistency-queries/DataFlowConsistency.ql +++ b/ruby/ql/consistency-queries/DataFlowConsistency.ql @@ -10,6 +10,6 @@ private class MyConsistencyConfiguration extends ConsistencyConfiguration { or n instanceof SummaryNode or - n instanceof SynthHashSplatArgumentsNode + n instanceof SynthHashSplatArgumentNode } } diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll index b19894bf2d1..a9103e5b666 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll @@ -241,7 +241,7 @@ private module Cached { TSummaryParameterNode(FlowSummaryImpl::Public::SummarizedCallable c, ParameterPosition pos) { FlowSummaryImpl::Private::summaryParameterNodeRange(c, pos) } or - TSynthHashSplatArgumentsNode(CfgNodes::ExprNodes::CallCfgNode c) { + TSynthHashSplatArgumentNode(CfgNodes::ExprNodes::CallCfgNode c) { exists(Argument arg | arg.isArgumentOf(c, any(ArgumentPosition pos | pos.isKeyword(_)))) } @@ -422,7 +422,7 @@ predicate nodeIsHidden(Node n) { or n instanceof SynthHashSplatParameterNode or - n instanceof SynthHashSplatArgumentsNode + n instanceof SynthHashSplatArgumentNode } /** An SSA definition, viewed as a node in a data flow graph. */ @@ -602,7 +602,7 @@ private module ParameterNodes { * ``` * * where direct keyword matching is possible, since we construct a synthesized hash - * splat argument (`SynthHashSplatArgumentsNode`) at the call site, which means that + * splat argument (`SynthHashSplatArgumentNode`) at the call site, which means that * `taint(1)` will flow into `p1` both via normal keyword matching and via the synthesized * nodes (and similarly for `p2`). However, this redunancy is OK since * (a) it means that type-tracking through keyword arguments also works in most cases, @@ -764,10 +764,10 @@ private module ArgumentNodes { * part of the method signature, such that those cannot end up in the hash-splat * parameter. */ - class SynthHashSplatArgumentsNode extends ArgumentNode, TSynthHashSplatArgumentsNode { + class SynthHashSplatArgumentNode extends ArgumentNode, TSynthHashSplatArgumentNode { CfgNodes::ExprNodes::CallCfgNode c; - SynthHashSplatArgumentsNode() { this = TSynthHashSplatArgumentsNode(c) } + SynthHashSplatArgumentNode() { this = TSynthHashSplatArgumentNode(c) } override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) { this.sourceArgumentOf(call.asCall(), pos) @@ -779,10 +779,10 @@ private module ArgumentNodes { } } - private class SynthHashSplatArgumentsNodeImpl extends NodeImpl, TSynthHashSplatArgumentsNode { + private class SynthHashSplatArgumentNodeImpl extends NodeImpl, TSynthHashSplatArgumentNode { CfgNodes::ExprNodes::CallCfgNode c; - SynthHashSplatArgumentsNodeImpl() { this = TSynthHashSplatArgumentsNode(c) } + SynthHashSplatArgumentNodeImpl() { this = TSynthHashSplatArgumentNode(c) } override CfgScope getCfgScope() { result = c.getExpr().getCfgScope() } @@ -1004,7 +1004,7 @@ predicate storeStep(Node node1, ContentSet c, Node node2) { or // Wrap all keyword arguments in a synthesized hash-splat argument node exists(CfgNodes::ExprNodes::CallCfgNode call, ArgumentPosition keywordPos, string name | - node2 = TSynthHashSplatArgumentsNode(call) and + node2 = TSynthHashSplatArgumentNode(call) and node1.asExpr().(Argument).isArgumentOf(call, keywordPos) and keywordPos.isKeyword(name) and c = getKeywordContent(name) From 50264547bfcc79fa5afa0ffc503eb011d5a4dace Mon Sep 17 00:00:00 2001 From: Evgenii Protsenko Date: Sun, 31 Oct 2021 13:22:31 +0300 Subject: [PATCH 483/505] make array taint-step better --- javascript/ql/lib/semmle/javascript/Arrays.qll | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/Arrays.qll b/javascript/ql/lib/semmle/javascript/Arrays.qll index c129e9a31b2..3598233f6ab 100644 --- a/javascript/ql/lib/semmle/javascript/Arrays.qll +++ b/javascript/ql/lib/semmle/javascript/Arrays.qll @@ -36,12 +36,18 @@ module ArrayTaintTracking { succ = call ) or - // `array.filter(x => x)` keeps the taint + // `array.filter(x => x)` and `array.filter(x => !!x)` keeps the taint call.(DataFlow::MethodCallNode).getMethodName() = "filter" and pred = call.getReceiver() and succ = call and - exists(DataFlow::FunctionNode callback | callback = call.getArgument(0).getAFunctionValue() | - callback.getParameter(0).getALocalUse() = callback.getAReturn() + exists(DataFlow::FunctionNode callback, DataFlow::Node param, DataFlow::Node ret | + callback = call.getArgument(0).getAFunctionValue() and + param = callback.getParameter(0).getALocalUse() and + ret = callback.getAReturn() + | + param = ret + or + param = DataFlow::exprNode(ret.asExpr().(LogNotExpr).getOperand().(LogNotExpr).getOperand()) ) or // `array.reduce` with tainted value in callback From a3cf81d419b78cfc14de80d75da63184942bf6e0 Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Mon, 13 Jun 2022 23:24:23 +0200 Subject: [PATCH 484/505] js: add filter taint test (post rebase conflicts) --- .../library-tests/Arrays/TaintFlow.expected | 26 ++ .../ql/test/library-tests/Arrays/TaintFlow.ql | 15 + .../ql/test/library-tests/Arrays/arrays.js | 3 + .../library-tests/Arrays/printAst.expected | 268 ++++++++++++------ 4 files changed, 223 insertions(+), 89 deletions(-) create mode 100644 javascript/ql/test/library-tests/Arrays/TaintFlow.expected create mode 100644 javascript/ql/test/library-tests/Arrays/TaintFlow.ql diff --git a/javascript/ql/test/library-tests/Arrays/TaintFlow.expected b/javascript/ql/test/library-tests/Arrays/TaintFlow.expected new file mode 100644 index 00000000000..20dbaa46ae2 --- /dev/null +++ b/javascript/ql/test/library-tests/Arrays/TaintFlow.expected @@ -0,0 +1,26 @@ +| arrays.js:2:16:2:23 | "source" | arrays.js:5:8:5:14 | obj.foo | +| arrays.js:2:16:2:23 | "source" | arrays.js:11:10:11:15 | arr[i] | +| arrays.js:2:16:2:23 | "source" | arrays.js:15:27:15:27 | e | +| arrays.js:2:16:2:23 | "source" | arrays.js:16:23:16:23 | e | +| arrays.js:2:16:2:23 | "source" | arrays.js:20:8:20:16 | arr.pop() | +| arrays.js:2:16:2:23 | "source" | arrays.js:49:8:49:13 | arr[0] | +| arrays.js:2:16:2:23 | "source" | arrays.js:52:10:52:10 | x | +| arrays.js:2:16:2:23 | "source" | arrays.js:56:10:56:10 | x | +| arrays.js:2:16:2:23 | "source" | arrays.js:60:10:60:10 | x | +| arrays.js:2:16:2:23 | "source" | arrays.js:66:10:66:10 | x | +| arrays.js:2:16:2:23 | "source" | arrays.js:71:10:71:10 | x | +| arrays.js:2:16:2:23 | "source" | arrays.js:74:8:74:29 | arr.fin ... llback) | +| arrays.js:2:16:2:23 | "source" | arrays.js:77:8:77:35 | arrayFi ... llback) | +| arrays.js:2:16:2:23 | "source" | arrays.js:81:10:81:10 | x | +| arrays.js:2:16:2:23 | "source" | arrays.js:84:8:84:17 | arr.at(-1) | +| arrays.js:18:22:18:29 | "source" | arrays.js:18:50:18:50 | e | +| arrays.js:22:15:22:22 | "source" | arrays.js:23:8:23:17 | arr2.pop() | +| arrays.js:25:15:25:22 | "source" | arrays.js:26:8:26:17 | arr3.pop() | +| arrays.js:29:21:29:28 | "source" | arrays.js:30:8:30:17 | arr4.pop() | +| arrays.js:29:21:29:28 | "source" | arrays.js:33:8:33:17 | arr5.pop() | +| arrays.js:29:21:29:28 | "source" | arrays.js:35:8:35:26 | arr5.slice(2).pop() | +| arrays.js:29:21:29:28 | "source" | arrays.js:41:8:41:17 | arr6.pop() | +| arrays.js:44:4:44:11 | "source" | arrays.js:45:10:45:18 | ary.pop() | +| arrays.js:44:4:44:11 | "source" | arrays.js:46:10:46:12 | ary | +| arrays.js:86:9:86:16 | "source" | arrays.js:86:8:86:34 | ["sourc ... ) => x) | +| arrays.js:87:9:87:16 | "source" | arrays.js:87:8:87:36 | ["sourc ... => !!x) | diff --git a/javascript/ql/test/library-tests/Arrays/TaintFlow.ql b/javascript/ql/test/library-tests/Arrays/TaintFlow.ql new file mode 100644 index 00000000000..694353873f2 --- /dev/null +++ b/javascript/ql/test/library-tests/Arrays/TaintFlow.ql @@ -0,0 +1,15 @@ +import javascript + +class ArrayTaintFlowConfig extends TaintTracking::Configuration { + ArrayTaintFlowConfig() { this = "ArrayTaintFlowConfig" } + + override predicate isSource(DataFlow::Node source) { source.asExpr().getStringValue() = "source" } + + override predicate isSink(DataFlow::Node sink) { + sink = any(DataFlow::CallNode call | call.getCalleeName() = "sink").getAnArgument() + } +} + +from ArrayTaintFlowConfig config, DataFlow::Node src, DataFlow::Node snk +where config.hasFlow(src, snk) +select src, snk diff --git a/javascript/ql/test/library-tests/Arrays/arrays.js b/javascript/ql/test/library-tests/Arrays/arrays.js index 2dfb203cd54..921f8730738 100644 --- a/javascript/ql/test/library-tests/Arrays/arrays.js +++ b/javascript/ql/test/library-tests/Arrays/arrays.js @@ -82,4 +82,7 @@ } sink(arr.at(-1)); // NOT OK + + sink(["source"].filter((x) => x)); // NOT OK + sink(["source"].filter((x) => !!x)); // NOT OK }); diff --git a/javascript/ql/test/library-tests/Arrays/printAst.expected b/javascript/ql/test/library-tests/Arrays/printAst.expected index c6e097c770e..46b124d85c3 100644 --- a/javascript/ql/test/library-tests/Arrays/printAst.expected +++ b/javascript/ql/test/library-tests/Arrays/printAst.expected @@ -1,9 +1,9 @@ nodes -| arrays.js:1:1:85:2 | [ParExpr] (functi ... T OK }) | semmle.label | [ParExpr] (functi ... T OK }) | -| arrays.js:1:1:85:3 | [ExprStmt] (functi ... OK }); | semmle.label | [ExprStmt] (functi ... OK }); | -| arrays.js:1:1:85:3 | [ExprStmt] (functi ... OK }); | semmle.order | 1 | -| arrays.js:1:2:85:1 | [FunctionExpr] functio ... OT OK } | semmle.label | [FunctionExpr] functio ... OT OK } | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | semmle.label | [BlockStmt] { let ... OT OK } | +| arrays.js:1:1:88:2 | [ParExpr] (functi ... T OK }) | semmle.label | [ParExpr] (functi ... T OK }) | +| arrays.js:1:1:88:3 | [ExprStmt] (functi ... OK }); | semmle.label | [ExprStmt] (functi ... OK }); | +| arrays.js:1:1:88:3 | [ExprStmt] (functi ... OK }); | semmle.order | 1 | +| arrays.js:1:2:88:1 | [FunctionExpr] functio ... OT OK } | semmle.label | [FunctionExpr] functio ... OT OK } | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | semmle.label | [BlockStmt] { let ... OT OK } | | arrays.js:2:3:2:24 | [DeclStmt] let source = ... | semmle.label | [DeclStmt] let source = ... | | arrays.js:2:7:2:12 | [VarDecl] source | semmle.label | [VarDecl] source | | arrays.js:2:7:2:23 | [VariableDeclarator] source = "source" | semmle.label | [VariableDeclarator] source = "source" | @@ -348,6 +348,30 @@ nodes | arrays.js:84:12:84:13 | [Label] at | semmle.label | [Label] at | | arrays.js:84:15:84:16 | [UnaryExpr] -1 | semmle.label | [UnaryExpr] -1 | | arrays.js:84:16:84:16 | [Literal] 1 | semmle.label | [Literal] 1 | +| arrays.js:86:3:86:6 | [VarRef] sink | semmle.label | [VarRef] sink | +| arrays.js:86:3:86:35 | [CallExpr] sink([" ... => x)) | semmle.label | [CallExpr] sink([" ... => x)) | +| arrays.js:86:3:86:36 | [ExprStmt] sink([" ... => x)); | semmle.label | [ExprStmt] sink([" ... => x)); | +| arrays.js:86:8:86:17 | [ArrayExpr] ["source"] | semmle.label | [ArrayExpr] ["source"] | +| arrays.js:86:8:86:24 | [DotExpr] ["source"].filter | semmle.label | [DotExpr] ["source"].filter | +| arrays.js:86:8:86:34 | [MethodCallExpr] ["sourc ... ) => x) | semmle.label | [MethodCallExpr] ["sourc ... ) => x) | +| arrays.js:86:9:86:16 | [Literal] "source" | semmle.label | [Literal] "source" | +| arrays.js:86:19:86:24 | [Label] filter | semmle.label | [Label] filter | +| arrays.js:86:26:86:33 | [ArrowFunctionExpr] (x) => x | semmle.label | [ArrowFunctionExpr] (x) => x | +| arrays.js:86:27:86:27 | [SimpleParameter] x | semmle.label | [SimpleParameter] x | +| arrays.js:86:33:86:33 | [VarRef] x | semmle.label | [VarRef] x | +| arrays.js:87:3:87:6 | [VarRef] sink | semmle.label | [VarRef] sink | +| arrays.js:87:3:87:37 | [CallExpr] sink([" ... > !!x)) | semmle.label | [CallExpr] sink([" ... > !!x)) | +| arrays.js:87:3:87:38 | [ExprStmt] sink([" ... !!x)); | semmle.label | [ExprStmt] sink([" ... !!x)); | +| arrays.js:87:8:87:17 | [ArrayExpr] ["source"] | semmle.label | [ArrayExpr] ["source"] | +| arrays.js:87:8:87:24 | [DotExpr] ["source"].filter | semmle.label | [DotExpr] ["source"].filter | +| arrays.js:87:8:87:36 | [MethodCallExpr] ["sourc ... => !!x) | semmle.label | [MethodCallExpr] ["sourc ... => !!x) | +| arrays.js:87:9:87:16 | [Literal] "source" | semmle.label | [Literal] "source" | +| arrays.js:87:19:87:24 | [Label] filter | semmle.label | [Label] filter | +| arrays.js:87:26:87:35 | [ArrowFunctionExpr] (x) => !!x | semmle.label | [ArrowFunctionExpr] (x) => !!x | +| arrays.js:87:27:87:27 | [SimpleParameter] x | semmle.label | [SimpleParameter] x | +| arrays.js:87:33:87:35 | [UnaryExpr] !!x | semmle.label | [UnaryExpr] !!x | +| arrays.js:87:34:87:35 | [UnaryExpr] !x | semmle.label | [UnaryExpr] !x | +| arrays.js:87:35:87:35 | [VarRef] x | semmle.label | [VarRef] x | | file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) | | file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) | | file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) | @@ -391,96 +415,106 @@ nodes | file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) | | file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) | | file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) | +| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) | +| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) | +| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) | +| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) | +| file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) | +| file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) | | file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) | | file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) | | file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) | | file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) | | file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) | edges -| arrays.js:1:1:85:2 | [ParExpr] (functi ... T OK }) | arrays.js:1:2:85:1 | [FunctionExpr] functio ... OT OK } | semmle.label | 1 | -| arrays.js:1:1:85:2 | [ParExpr] (functi ... T OK }) | arrays.js:1:2:85:1 | [FunctionExpr] functio ... OT OK } | semmle.order | 1 | -| arrays.js:1:1:85:3 | [ExprStmt] (functi ... OK }); | arrays.js:1:1:85:2 | [ParExpr] (functi ... T OK }) | semmle.label | 1 | -| arrays.js:1:1:85:3 | [ExprStmt] (functi ... OK }); | arrays.js:1:1:85:2 | [ParExpr] (functi ... T OK }) | semmle.order | 1 | -| arrays.js:1:2:85:1 | [FunctionExpr] functio ... OT OK } | arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | semmle.label | 5 | -| arrays.js:1:2:85:1 | [FunctionExpr] functio ... OT OK } | arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | semmle.order | 5 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:2:3:2:24 | [DeclStmt] let source = ... | semmle.label | 1 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:2:3:2:24 | [DeclStmt] let source = ... | semmle.order | 1 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:4:3:4:28 | [DeclStmt] var obj = ... | semmle.label | 2 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:4:3:4:28 | [DeclStmt] var obj = ... | semmle.order | 2 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:5:3:5:16 | [ExprStmt] sink(obj.foo); | semmle.label | 3 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:5:3:5:16 | [ExprStmt] sink(obj.foo); | semmle.order | 3 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:7:3:7:15 | [DeclStmt] var arr = ... | semmle.label | 4 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:7:3:7:15 | [DeclStmt] var arr = ... | semmle.order | 4 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:8:3:8:19 | [ExprStmt] arr.push(source); | semmle.label | 5 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:8:3:8:19 | [ExprStmt] arr.push(source); | semmle.order | 5 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:10:3:12:3 | [ForStmt] for (va ... OK } | semmle.label | 6 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:10:3:12:3 | [ForStmt] for (va ... OK } | semmle.order | 6 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:15:3:15:30 | [ExprStmt] arr.for ... nk(e)); | semmle.label | 7 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:15:3:15:30 | [ExprStmt] arr.for ... nk(e)); | semmle.order | 7 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:16:3:16:26 | [ExprStmt] arr.map ... nk(e)); | semmle.label | 8 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:16:3:16:26 | [ExprStmt] arr.map ... nk(e)); | semmle.order | 8 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:18:3:18:53 | [ExprStmt] [1, 2, ... nk(e)); | semmle.label | 9 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:18:3:18:53 | [ExprStmt] [1, 2, ... nk(e)); | semmle.order | 9 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:20:3:20:18 | [ExprStmt] sink(arr.pop()); | semmle.label | 10 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:20:3:20:18 | [ExprStmt] sink(arr.pop()); | semmle.order | 10 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:22:3:22:24 | [DeclStmt] var arr2 = ... | semmle.label | 11 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:22:3:22:24 | [DeclStmt] var arr2 = ... | semmle.order | 11 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:23:3:23:19 | [ExprStmt] sink(arr2.pop()); | semmle.label | 12 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:23:3:23:19 | [ExprStmt] sink(arr2.pop()); | semmle.order | 12 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:25:3:25:24 | [DeclStmt] var arr3 = ... | semmle.label | 13 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:25:3:25:24 | [DeclStmt] var arr3 = ... | semmle.order | 13 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:26:3:26:19 | [ExprStmt] sink(arr3.pop()); | semmle.label | 14 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:26:3:26:19 | [ExprStmt] sink(arr3.pop()); | semmle.order | 14 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:28:3:28:16 | [DeclStmt] var arr4 = ... | semmle.label | 15 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:28:3:28:16 | [DeclStmt] var arr4 = ... | semmle.order | 15 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:29:3:29:30 | [ExprStmt] arr4.sp ... urce"); | semmle.label | 16 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:29:3:29:30 | [ExprStmt] arr4.sp ... urce"); | semmle.order | 16 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:30:3:30:19 | [ExprStmt] sink(arr4.pop()); | semmle.label | 17 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:30:3:30:19 | [ExprStmt] sink(arr4.pop()); | semmle.order | 17 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:32:3:32:29 | [DeclStmt] var arr5 = ... | semmle.label | 18 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:32:3:32:29 | [DeclStmt] var arr5 = ... | semmle.order | 18 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:33:3:33:19 | [ExprStmt] sink(arr5.pop()); | semmle.label | 19 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:33:3:33:19 | [ExprStmt] sink(arr5.pop()); | semmle.order | 19 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:35:3:35:28 | [ExprStmt] sink(ar ... pop()); | semmle.label | 20 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:35:3:35:28 | [ExprStmt] sink(ar ... pop()); | semmle.order | 20 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:37:3:37:16 | [DeclStmt] var arr6 = ... | semmle.label | 21 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:37:3:37:16 | [DeclStmt] var arr6 = ... | semmle.order | 21 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:38:3:40:3 | [ForStmt] for (va ... i]; } | semmle.label | 22 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:38:3:40:3 | [ForStmt] for (va ... i]; } | semmle.order | 22 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:41:3:41:19 | [ExprStmt] sink(arr6.pop()); | semmle.label | 23 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:41:3:41:19 | [ExprStmt] sink(arr6.pop()); | semmle.order | 23 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:44:3:47:5 | [ExprStmt] ["sourc ... . }); | semmle.label | 24 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:44:3:47:5 | [ExprStmt] ["sourc ... . }); | semmle.order | 24 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:49:3:49:15 | [ExprStmt] sink(arr[0]); | semmle.label | 25 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:49:3:49:15 | [ExprStmt] sink(arr[0]); | semmle.order | 25 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:51:3:53:3 | [ForOfStmt] for (co ... OK } | semmle.label | 26 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:51:3:53:3 | [ForOfStmt] for (co ... OK } | semmle.order | 26 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:55:3:57:3 | [ForOfStmt] for (co ... OK } | semmle.label | 27 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:55:3:57:3 | [ForOfStmt] for (co ... OK } | semmle.order | 27 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:59:3:61:3 | [ForOfStmt] for (co ... OK } | semmle.label | 28 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:59:3:61:3 | [ForOfStmt] for (co ... OK } | semmle.order | 28 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:63:3:63:16 | [DeclStmt] var arr7 = ... | semmle.label | 29 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:63:3:63:16 | [DeclStmt] var arr7 = ... | semmle.order | 29 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:64:3:64:20 | [ExprStmt] arr7.push(...arr); | semmle.label | 30 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:64:3:64:20 | [ExprStmt] arr7.push(...arr); | semmle.order | 30 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:65:3:67:3 | [ForOfStmt] for (co ... OK } | semmle.label | 31 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:65:3:67:3 | [ForOfStmt] for (co ... OK } | semmle.order | 31 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:69:3:69:42 | [DeclStmt] const arrayFrom = ... | semmle.label | 32 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:69:3:69:42 | [DeclStmt] const arrayFrom = ... | semmle.order | 32 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:70:3:72:3 | [ForOfStmt] for (co ... OK } | semmle.label | 33 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:70:3:72:3 | [ForOfStmt] for (co ... OK } | semmle.order | 33 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:74:3:74:31 | [ExprStmt] sink(ar ... back)); | semmle.label | 34 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:74:3:74:31 | [ExprStmt] sink(ar ... back)); | semmle.order | 34 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:76:3:76:42 | [DeclStmt] const arrayFind = ... | semmle.label | 35 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:76:3:76:42 | [DeclStmt] const arrayFind = ... | semmle.order | 35 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:77:3:77:37 | [ExprStmt] sink(ar ... back)); | semmle.label | 36 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:77:3:77:37 | [ExprStmt] sink(ar ... back)); | semmle.order | 36 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:79:3:79:31 | [DeclStmt] const uniq = ... | semmle.label | 37 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:79:3:79:31 | [DeclStmt] const uniq = ... | semmle.order | 37 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:80:3:82:3 | [ForOfStmt] for (co ... OK } | semmle.label | 38 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:80:3:82:3 | [ForOfStmt] for (co ... OK } | semmle.order | 38 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:84:3:84:19 | [ExprStmt] sink(arr.at(-1)); | semmle.label | 39 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:84:3:84:19 | [ExprStmt] sink(arr.at(-1)); | semmle.order | 39 | +| arrays.js:1:1:88:2 | [ParExpr] (functi ... T OK }) | arrays.js:1:2:88:1 | [FunctionExpr] functio ... OT OK } | semmle.label | 1 | +| arrays.js:1:1:88:2 | [ParExpr] (functi ... T OK }) | arrays.js:1:2:88:1 | [FunctionExpr] functio ... OT OK } | semmle.order | 1 | +| arrays.js:1:1:88:3 | [ExprStmt] (functi ... OK }); | arrays.js:1:1:88:2 | [ParExpr] (functi ... T OK }) | semmle.label | 1 | +| arrays.js:1:1:88:3 | [ExprStmt] (functi ... OK }); | arrays.js:1:1:88:2 | [ParExpr] (functi ... T OK }) | semmle.order | 1 | +| arrays.js:1:2:88:1 | [FunctionExpr] functio ... OT OK } | arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | semmle.label | 5 | +| arrays.js:1:2:88:1 | [FunctionExpr] functio ... OT OK } | arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | semmle.order | 5 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:2:3:2:24 | [DeclStmt] let source = ... | semmle.label | 1 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:2:3:2:24 | [DeclStmt] let source = ... | semmle.order | 1 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:4:3:4:28 | [DeclStmt] var obj = ... | semmle.label | 2 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:4:3:4:28 | [DeclStmt] var obj = ... | semmle.order | 2 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:5:3:5:16 | [ExprStmt] sink(obj.foo); | semmle.label | 3 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:5:3:5:16 | [ExprStmt] sink(obj.foo); | semmle.order | 3 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:7:3:7:15 | [DeclStmt] var arr = ... | semmle.label | 4 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:7:3:7:15 | [DeclStmt] var arr = ... | semmle.order | 4 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:8:3:8:19 | [ExprStmt] arr.push(source); | semmle.label | 5 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:8:3:8:19 | [ExprStmt] arr.push(source); | semmle.order | 5 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:10:3:12:3 | [ForStmt] for (va ... OK } | semmle.label | 6 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:10:3:12:3 | [ForStmt] for (va ... OK } | semmle.order | 6 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:15:3:15:30 | [ExprStmt] arr.for ... nk(e)); | semmle.label | 7 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:15:3:15:30 | [ExprStmt] arr.for ... nk(e)); | semmle.order | 7 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:16:3:16:26 | [ExprStmt] arr.map ... nk(e)); | semmle.label | 8 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:16:3:16:26 | [ExprStmt] arr.map ... nk(e)); | semmle.order | 8 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:18:3:18:53 | [ExprStmt] [1, 2, ... nk(e)); | semmle.label | 9 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:18:3:18:53 | [ExprStmt] [1, 2, ... nk(e)); | semmle.order | 9 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:20:3:20:18 | [ExprStmt] sink(arr.pop()); | semmle.label | 10 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:20:3:20:18 | [ExprStmt] sink(arr.pop()); | semmle.order | 10 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:22:3:22:24 | [DeclStmt] var arr2 = ... | semmle.label | 11 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:22:3:22:24 | [DeclStmt] var arr2 = ... | semmle.order | 11 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:23:3:23:19 | [ExprStmt] sink(arr2.pop()); | semmle.label | 12 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:23:3:23:19 | [ExprStmt] sink(arr2.pop()); | semmle.order | 12 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:25:3:25:24 | [DeclStmt] var arr3 = ... | semmle.label | 13 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:25:3:25:24 | [DeclStmt] var arr3 = ... | semmle.order | 13 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:26:3:26:19 | [ExprStmt] sink(arr3.pop()); | semmle.label | 14 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:26:3:26:19 | [ExprStmt] sink(arr3.pop()); | semmle.order | 14 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:28:3:28:16 | [DeclStmt] var arr4 = ... | semmle.label | 15 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:28:3:28:16 | [DeclStmt] var arr4 = ... | semmle.order | 15 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:29:3:29:30 | [ExprStmt] arr4.sp ... urce"); | semmle.label | 16 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:29:3:29:30 | [ExprStmt] arr4.sp ... urce"); | semmle.order | 16 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:30:3:30:19 | [ExprStmt] sink(arr4.pop()); | semmle.label | 17 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:30:3:30:19 | [ExprStmt] sink(arr4.pop()); | semmle.order | 17 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:32:3:32:29 | [DeclStmt] var arr5 = ... | semmle.label | 18 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:32:3:32:29 | [DeclStmt] var arr5 = ... | semmle.order | 18 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:33:3:33:19 | [ExprStmt] sink(arr5.pop()); | semmle.label | 19 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:33:3:33:19 | [ExprStmt] sink(arr5.pop()); | semmle.order | 19 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:35:3:35:28 | [ExprStmt] sink(ar ... pop()); | semmle.label | 20 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:35:3:35:28 | [ExprStmt] sink(ar ... pop()); | semmle.order | 20 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:37:3:37:16 | [DeclStmt] var arr6 = ... | semmle.label | 21 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:37:3:37:16 | [DeclStmt] var arr6 = ... | semmle.order | 21 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:38:3:40:3 | [ForStmt] for (va ... i]; } | semmle.label | 22 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:38:3:40:3 | [ForStmt] for (va ... i]; } | semmle.order | 22 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:41:3:41:19 | [ExprStmt] sink(arr6.pop()); | semmle.label | 23 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:41:3:41:19 | [ExprStmt] sink(arr6.pop()); | semmle.order | 23 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:44:3:47:5 | [ExprStmt] ["sourc ... . }); | semmle.label | 24 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:44:3:47:5 | [ExprStmt] ["sourc ... . }); | semmle.order | 24 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:49:3:49:15 | [ExprStmt] sink(arr[0]); | semmle.label | 25 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:49:3:49:15 | [ExprStmt] sink(arr[0]); | semmle.order | 25 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:51:3:53:3 | [ForOfStmt] for (co ... OK } | semmle.label | 26 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:51:3:53:3 | [ForOfStmt] for (co ... OK } | semmle.order | 26 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:55:3:57:3 | [ForOfStmt] for (co ... OK } | semmle.label | 27 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:55:3:57:3 | [ForOfStmt] for (co ... OK } | semmle.order | 27 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:59:3:61:3 | [ForOfStmt] for (co ... OK } | semmle.label | 28 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:59:3:61:3 | [ForOfStmt] for (co ... OK } | semmle.order | 28 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:63:3:63:16 | [DeclStmt] var arr7 = ... | semmle.label | 29 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:63:3:63:16 | [DeclStmt] var arr7 = ... | semmle.order | 29 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:64:3:64:20 | [ExprStmt] arr7.push(...arr); | semmle.label | 30 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:64:3:64:20 | [ExprStmt] arr7.push(...arr); | semmle.order | 30 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:65:3:67:3 | [ForOfStmt] for (co ... OK } | semmle.label | 31 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:65:3:67:3 | [ForOfStmt] for (co ... OK } | semmle.order | 31 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:69:3:69:42 | [DeclStmt] const arrayFrom = ... | semmle.label | 32 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:69:3:69:42 | [DeclStmt] const arrayFrom = ... | semmle.order | 32 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:70:3:72:3 | [ForOfStmt] for (co ... OK } | semmle.label | 33 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:70:3:72:3 | [ForOfStmt] for (co ... OK } | semmle.order | 33 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:74:3:74:31 | [ExprStmt] sink(ar ... back)); | semmle.label | 34 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:74:3:74:31 | [ExprStmt] sink(ar ... back)); | semmle.order | 34 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:76:3:76:42 | [DeclStmt] const arrayFind = ... | semmle.label | 35 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:76:3:76:42 | [DeclStmt] const arrayFind = ... | semmle.order | 35 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:77:3:77:37 | [ExprStmt] sink(ar ... back)); | semmle.label | 36 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:77:3:77:37 | [ExprStmt] sink(ar ... back)); | semmle.order | 36 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:79:3:79:31 | [DeclStmt] const uniq = ... | semmle.label | 37 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:79:3:79:31 | [DeclStmt] const uniq = ... | semmle.order | 37 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:80:3:82:3 | [ForOfStmt] for (co ... OK } | semmle.label | 38 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:80:3:82:3 | [ForOfStmt] for (co ... OK } | semmle.order | 38 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:84:3:84:19 | [ExprStmt] sink(arr.at(-1)); | semmle.label | 39 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:84:3:84:19 | [ExprStmt] sink(arr.at(-1)); | semmle.order | 39 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:86:3:86:36 | [ExprStmt] sink([" ... => x)); | semmle.label | 40 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:86:3:86:36 | [ExprStmt] sink([" ... => x)); | semmle.order | 40 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:87:3:87:38 | [ExprStmt] sink([" ... !!x)); | semmle.label | 41 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:87:3:87:38 | [ExprStmt] sink([" ... !!x)); | semmle.order | 41 | | arrays.js:2:3:2:24 | [DeclStmt] let source = ... | arrays.js:2:7:2:23 | [VariableDeclarator] source = "source" | semmle.label | 1 | | arrays.js:2:3:2:24 | [DeclStmt] let source = ... | arrays.js:2:7:2:23 | [VariableDeclarator] source = "source" | semmle.order | 1 | | arrays.js:2:7:2:23 | [VariableDeclarator] source = "source" | arrays.js:2:7:2:12 | [VarDecl] source | semmle.label | 1 | @@ -1081,6 +1115,50 @@ edges | arrays.js:84:8:84:17 | [MethodCallExpr] arr.at(-1) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 | | arrays.js:84:15:84:16 | [UnaryExpr] -1 | arrays.js:84:16:84:16 | [Literal] 1 | semmle.label | 1 | | arrays.js:84:15:84:16 | [UnaryExpr] -1 | arrays.js:84:16:84:16 | [Literal] 1 | semmle.order | 1 | +| arrays.js:86:3:86:35 | [CallExpr] sink([" ... => x)) | arrays.js:86:3:86:6 | [VarRef] sink | semmle.label | 0 | +| arrays.js:86:3:86:35 | [CallExpr] sink([" ... => x)) | arrays.js:86:3:86:6 | [VarRef] sink | semmle.order | 0 | +| arrays.js:86:3:86:35 | [CallExpr] sink([" ... => x)) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 | +| arrays.js:86:3:86:35 | [CallExpr] sink([" ... => x)) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 | +| arrays.js:86:3:86:36 | [ExprStmt] sink([" ... => x)); | arrays.js:86:3:86:35 | [CallExpr] sink([" ... => x)) | semmle.label | 1 | +| arrays.js:86:3:86:36 | [ExprStmt] sink([" ... => x)); | arrays.js:86:3:86:35 | [CallExpr] sink([" ... => x)) | semmle.order | 1 | +| arrays.js:86:8:86:17 | [ArrayExpr] ["source"] | arrays.js:86:9:86:16 | [Literal] "source" | semmle.label | 1 | +| arrays.js:86:8:86:17 | [ArrayExpr] ["source"] | arrays.js:86:9:86:16 | [Literal] "source" | semmle.order | 1 | +| arrays.js:86:8:86:24 | [DotExpr] ["source"].filter | arrays.js:86:8:86:17 | [ArrayExpr] ["source"] | semmle.label | 1 | +| arrays.js:86:8:86:24 | [DotExpr] ["source"].filter | arrays.js:86:8:86:17 | [ArrayExpr] ["source"] | semmle.order | 1 | +| arrays.js:86:8:86:24 | [DotExpr] ["source"].filter | arrays.js:86:19:86:24 | [Label] filter | semmle.label | 2 | +| arrays.js:86:8:86:24 | [DotExpr] ["source"].filter | arrays.js:86:19:86:24 | [Label] filter | semmle.order | 2 | +| arrays.js:86:8:86:34 | [MethodCallExpr] ["sourc ... ) => x) | arrays.js:86:8:86:24 | [DotExpr] ["source"].filter | semmle.label | 0 | +| arrays.js:86:8:86:34 | [MethodCallExpr] ["sourc ... ) => x) | arrays.js:86:8:86:24 | [DotExpr] ["source"].filter | semmle.order | 0 | +| arrays.js:86:8:86:34 | [MethodCallExpr] ["sourc ... ) => x) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 | +| arrays.js:86:8:86:34 | [MethodCallExpr] ["sourc ... ) => x) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 | +| arrays.js:86:26:86:33 | [ArrowFunctionExpr] (x) => x | arrays.js:86:33:86:33 | [VarRef] x | semmle.label | 5 | +| arrays.js:86:26:86:33 | [ArrowFunctionExpr] (x) => x | arrays.js:86:33:86:33 | [VarRef] x | semmle.order | 5 | +| arrays.js:86:26:86:33 | [ArrowFunctionExpr] (x) => x | file://:0:0:0:0 | (Parameters) | semmle.label | 1 | +| arrays.js:86:26:86:33 | [ArrowFunctionExpr] (x) => x | file://:0:0:0:0 | (Parameters) | semmle.order | 1 | +| arrays.js:87:3:87:37 | [CallExpr] sink([" ... > !!x)) | arrays.js:87:3:87:6 | [VarRef] sink | semmle.label | 0 | +| arrays.js:87:3:87:37 | [CallExpr] sink([" ... > !!x)) | arrays.js:87:3:87:6 | [VarRef] sink | semmle.order | 0 | +| arrays.js:87:3:87:37 | [CallExpr] sink([" ... > !!x)) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 | +| arrays.js:87:3:87:37 | [CallExpr] sink([" ... > !!x)) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 | +| arrays.js:87:3:87:38 | [ExprStmt] sink([" ... !!x)); | arrays.js:87:3:87:37 | [CallExpr] sink([" ... > !!x)) | semmle.label | 1 | +| arrays.js:87:3:87:38 | [ExprStmt] sink([" ... !!x)); | arrays.js:87:3:87:37 | [CallExpr] sink([" ... > !!x)) | semmle.order | 1 | +| arrays.js:87:8:87:17 | [ArrayExpr] ["source"] | arrays.js:87:9:87:16 | [Literal] "source" | semmle.label | 1 | +| arrays.js:87:8:87:17 | [ArrayExpr] ["source"] | arrays.js:87:9:87:16 | [Literal] "source" | semmle.order | 1 | +| arrays.js:87:8:87:24 | [DotExpr] ["source"].filter | arrays.js:87:8:87:17 | [ArrayExpr] ["source"] | semmle.label | 1 | +| arrays.js:87:8:87:24 | [DotExpr] ["source"].filter | arrays.js:87:8:87:17 | [ArrayExpr] ["source"] | semmle.order | 1 | +| arrays.js:87:8:87:24 | [DotExpr] ["source"].filter | arrays.js:87:19:87:24 | [Label] filter | semmle.label | 2 | +| arrays.js:87:8:87:24 | [DotExpr] ["source"].filter | arrays.js:87:19:87:24 | [Label] filter | semmle.order | 2 | +| arrays.js:87:8:87:36 | [MethodCallExpr] ["sourc ... => !!x) | arrays.js:87:8:87:24 | [DotExpr] ["source"].filter | semmle.label | 0 | +| arrays.js:87:8:87:36 | [MethodCallExpr] ["sourc ... => !!x) | arrays.js:87:8:87:24 | [DotExpr] ["source"].filter | semmle.order | 0 | +| arrays.js:87:8:87:36 | [MethodCallExpr] ["sourc ... => !!x) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 | +| arrays.js:87:8:87:36 | [MethodCallExpr] ["sourc ... => !!x) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 | +| arrays.js:87:26:87:35 | [ArrowFunctionExpr] (x) => !!x | arrays.js:87:33:87:35 | [UnaryExpr] !!x | semmle.label | 5 | +| arrays.js:87:26:87:35 | [ArrowFunctionExpr] (x) => !!x | arrays.js:87:33:87:35 | [UnaryExpr] !!x | semmle.order | 5 | +| arrays.js:87:26:87:35 | [ArrowFunctionExpr] (x) => !!x | file://:0:0:0:0 | (Parameters) | semmle.label | 1 | +| arrays.js:87:26:87:35 | [ArrowFunctionExpr] (x) => !!x | file://:0:0:0:0 | (Parameters) | semmle.order | 1 | +| arrays.js:87:33:87:35 | [UnaryExpr] !!x | arrays.js:87:34:87:35 | [UnaryExpr] !x | semmle.label | 1 | +| arrays.js:87:33:87:35 | [UnaryExpr] !!x | arrays.js:87:34:87:35 | [UnaryExpr] !x | semmle.order | 1 | +| arrays.js:87:34:87:35 | [UnaryExpr] !x | arrays.js:87:35:87:35 | [VarRef] x | semmle.label | 1 | +| arrays.js:87:34:87:35 | [UnaryExpr] !x | arrays.js:87:35:87:35 | [VarRef] x | semmle.order | 1 | | file://:0:0:0:0 | (Arguments) | arrays.js:5:8:5:14 | [DotExpr] obj.foo | semmle.label | 0 | | file://:0:0:0:0 | (Arguments) | arrays.js:5:8:5:14 | [DotExpr] obj.foo | semmle.order | 0 | | file://:0:0:0:0 | (Arguments) | arrays.js:8:12:8:17 | [VarRef] source | semmle.label | 0 | @@ -1173,6 +1251,14 @@ edges | file://:0:0:0:0 | (Arguments) | arrays.js:84:8:84:17 | [MethodCallExpr] arr.at(-1) | semmle.order | 0 | | file://:0:0:0:0 | (Arguments) | arrays.js:84:15:84:16 | [UnaryExpr] -1 | semmle.label | 0 | | file://:0:0:0:0 | (Arguments) | arrays.js:84:15:84:16 | [UnaryExpr] -1 | semmle.order | 0 | +| file://:0:0:0:0 | (Arguments) | arrays.js:86:8:86:34 | [MethodCallExpr] ["sourc ... ) => x) | semmle.label | 0 | +| file://:0:0:0:0 | (Arguments) | arrays.js:86:8:86:34 | [MethodCallExpr] ["sourc ... ) => x) | semmle.order | 0 | +| file://:0:0:0:0 | (Arguments) | arrays.js:86:26:86:33 | [ArrowFunctionExpr] (x) => x | semmle.label | 0 | +| file://:0:0:0:0 | (Arguments) | arrays.js:86:26:86:33 | [ArrowFunctionExpr] (x) => x | semmle.order | 0 | +| file://:0:0:0:0 | (Arguments) | arrays.js:87:8:87:36 | [MethodCallExpr] ["sourc ... => !!x) | semmle.label | 0 | +| file://:0:0:0:0 | (Arguments) | arrays.js:87:8:87:36 | [MethodCallExpr] ["sourc ... => !!x) | semmle.order | 0 | +| file://:0:0:0:0 | (Arguments) | arrays.js:87:26:87:35 | [ArrowFunctionExpr] (x) => !!x | semmle.label | 0 | +| file://:0:0:0:0 | (Arguments) | arrays.js:87:26:87:35 | [ArrowFunctionExpr] (x) => !!x | semmle.order | 0 | | file://:0:0:0:0 | (Parameters) | arrays.js:15:16:15:16 | [SimpleParameter] e | semmle.label | 0 | | file://:0:0:0:0 | (Parameters) | arrays.js:15:16:15:16 | [SimpleParameter] e | semmle.order | 0 | | file://:0:0:0:0 | (Parameters) | arrays.js:16:12:16:12 | [SimpleParameter] e | semmle.label | 0 | @@ -1187,5 +1273,9 @@ edges | file://:0:0:0:0 | (Parameters) | arrays.js:44:26:44:26 | [SimpleParameter] i | semmle.order | 1 | | file://:0:0:0:0 | (Parameters) | arrays.js:44:29:44:31 | [SimpleParameter] ary | semmle.label | 2 | | file://:0:0:0:0 | (Parameters) | arrays.js:44:29:44:31 | [SimpleParameter] ary | semmle.order | 2 | +| file://:0:0:0:0 | (Parameters) | arrays.js:86:27:86:27 | [SimpleParameter] x | semmle.label | 0 | +| file://:0:0:0:0 | (Parameters) | arrays.js:86:27:86:27 | [SimpleParameter] x | semmle.order | 0 | +| file://:0:0:0:0 | (Parameters) | arrays.js:87:27:87:27 | [SimpleParameter] x | semmle.label | 0 | +| file://:0:0:0:0 | (Parameters) | arrays.js:87:27:87:27 | [SimpleParameter] x | semmle.order | 0 | graphProperties | semmle.graphKind | tree | From 5c3d39579a20bbd08be93a84a376cd1bc9b59fc9 Mon Sep 17 00:00:00 2001 From: Edoardo Pirovano Date: Tue, 2 Aug 2022 13:07:42 +0100 Subject: [PATCH 485/505] JS: Change how TRAP cache is configured --- .../com/semmle/js/extractor/AutoBuild.java | 31 ++----------------- .../js/extractor/ExtractorOptionsUtil.java | 12 +++++++ .../src/com/semmle/js/extractor/Main.java | 27 +--------------- .../extractor/trapcache/DefaultTrapCache.java | 12 +++++-- .../js/extractor/trapcache/ITrapCache.java | 29 +++++++++++++++++ 5 files changed, 55 insertions(+), 56 deletions(-) create mode 100644 javascript/extractor/src/com/semmle/js/extractor/ExtractorOptionsUtil.java diff --git a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java index 8eadda7e63a..101b8094fdd 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java +++ b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java @@ -86,8 +86,6 @@ import com.semmle.util.trap.TrapWriter; * XML is also supported *

  • LGTM_INDEX_XML_MODE: whether to extract XML files *
  • LGTM_THREADS: the maximum number of files to extract in parallel - *
  • LGTM_TRAP_CACHE: the path of a directory to use for trap caching - *
  • LGTM_TRAP_CACHE_BOUND: the size to bound the trap cache to * * *

    It extracts the following: @@ -220,7 +218,7 @@ public class AutoBuild { this.LGTM_SRC = toRealPath(getPathFromEnvVar("LGTM_SRC")); this.SEMMLE_DIST = Paths.get(EnvironmentVariables.getExtractorRoot()); this.outputConfig = new ExtractorOutputConfig(LegacyLanguage.JAVASCRIPT); - this.trapCache = mkTrapCache(); + this.trapCache = ITrapCache.fromExtractorOptions(); this.typeScriptMode = getEnumFromEnvVar("LGTM_INDEX_TYPESCRIPT", TypeScriptMode.class, TypeScriptMode.FULL); this.defaultEncoding = getEnvVar("LGTM_INDEX_DEFAULT_ENCODING"); @@ -281,28 +279,6 @@ public class AutoBuild { } } - /** - * Set up TRAP cache based on environment variables LGTM_TRAP_CACHE and - * LGTM_TRAP_CACHE_BOUND. - */ - private ITrapCache mkTrapCache() { - ITrapCache trapCache; - String trapCachePath = getEnvVar("LGTM_TRAP_CACHE"); - if (trapCachePath != null) { - Long sizeBound = null; - String trapCacheBound = getEnvVar("LGTM_TRAP_CACHE_BOUND"); - if (trapCacheBound != null) { - sizeBound = DefaultTrapCache.asFileSize(trapCacheBound); - if (sizeBound == null) - throw new UserError("Invalid TRAP cache size bound: " + trapCacheBound); - } - trapCache = new DefaultTrapCache(trapCachePath, sizeBound, Main.EXTRACTOR_VERSION); - } else { - trapCache = new DummyTrapCache(); - } - return trapCache; - } - private void setupFileTypes() { for (String spec : Main.NEWLINE.split(getEnvVar("LGTM_INDEX_FILETYPES", ""))) { spec = spec.trim(); @@ -513,14 +489,13 @@ public class AutoBuild { SEMMLE_DIST.resolve(".cache").resolve("trap-cache").resolve("javascript"); if (Files.isDirectory(trapCachePath)) { trapCache = - new DefaultTrapCache(trapCachePath.toString(), null, Main.EXTRACTOR_VERSION) { + new DefaultTrapCache(trapCachePath.toString(), null, Main.EXTRACTOR_VERSION, false) { boolean warnedAboutCacheMiss = false; @Override public File lookup(String source, ExtractorConfig config, FileType type) { File f = super.lookup(source, config, type); - // only return `f` if it exists; this has the effect of making the cache read-only - if (f.exists()) return f; + if (f != null) return f; // warn on first failed lookup if (!warnedAboutCacheMiss) { warn("Trap cache lookup for externs failed."); diff --git a/javascript/extractor/src/com/semmle/js/extractor/ExtractorOptionsUtil.java b/javascript/extractor/src/com/semmle/js/extractor/ExtractorOptionsUtil.java new file mode 100644 index 00000000000..c58f27b2c8a --- /dev/null +++ b/javascript/extractor/src/com/semmle/js/extractor/ExtractorOptionsUtil.java @@ -0,0 +1,12 @@ +package com.semmle.js.extractor; + +import com.semmle.util.process.Env; + +public class ExtractorOptionsUtil { + public static String readExtractorOption(String... option) { + StringBuilder name = new StringBuilder("CODEQL_EXTRACTOR_JAVASCRIPT_OPTION"); + for (String segment : option) + name.append("_").append(segment.toUpperCase()); + return Env.systemEnv().getNonEmpty(name.toString()); + } +} diff --git a/javascript/extractor/src/com/semmle/js/extractor/Main.java b/javascript/extractor/src/com/semmle/js/extractor/Main.java index c001d22dfa9..9929195bb77 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/Main.java +++ b/javascript/extractor/src/com/semmle/js/extractor/Main.java @@ -15,8 +15,6 @@ import com.semmle.extractor.html.HtmlPopulator; import com.semmle.js.extractor.ExtractorConfig.Platform; import com.semmle.js.extractor.ExtractorConfig.SourceType; import com.semmle.js.extractor.FileExtractor.FileType; -import com.semmle.js.extractor.trapcache.DefaultTrapCache; -import com.semmle.js.extractor.trapcache.DummyTrapCache; import com.semmle.js.extractor.trapcache.ITrapCache; import com.semmle.js.parser.ParsedProject; import com.semmle.ts.extractor.TypeExtractor; @@ -61,8 +59,6 @@ public class Main { private static final String P_PLATFORM = "--platform"; private static final String P_QUIET = "--quiet"; private static final String P_SOURCE_TYPE = "--source-type"; - private static final String P_TRAP_CACHE = "--trap-cache"; - private static final String P_TRAP_CACHE_BOUND = "--trap-cache-bound"; private static final String P_TYPESCRIPT = "--typescript"; private static final String P_TYPESCRIPT_FULL = "--typescript-full"; private static final String P_TYPESCRIPT_RAM = "--typescript-ram"; @@ -112,22 +108,7 @@ public class Main { ap.parse(); extractorConfig = parseJSOptions(ap); - ITrapCache trapCache; - if (ap.has(P_TRAP_CACHE)) { - Long sizeBound = null; - if (ap.has(P_TRAP_CACHE_BOUND)) { - String tcb = ap.getString(P_TRAP_CACHE_BOUND); - sizeBound = DefaultTrapCache.asFileSize(tcb); - if (sizeBound == null) ap.error("Invalid TRAP cache size bound: " + tcb); - } - trapCache = new DefaultTrapCache(ap.getString(P_TRAP_CACHE), sizeBound, EXTRACTOR_VERSION); - } else { - if (ap.has(P_TRAP_CACHE_BOUND)) - ap.error( - P_TRAP_CACHE_BOUND + " should only be specified together with " + P_TRAP_CACHE + "."); - trapCache = new DummyTrapCache(); - } - fileExtractor = new FileExtractor(extractorConfig, extractorOutputConfig, trapCache); + fileExtractor = new FileExtractor(extractorConfig, extractorOutputConfig, ITrapCache.fromExtractorOptions()); setupMatchers(ap); @@ -432,12 +413,6 @@ public class Main { argsParser.addToleratedFlag(P_TOLERATE_PARSE_ERRORS, 0); argsParser.addFlag( P_ABORT_ON_PARSE_ERRORS, 0, "Abort extraction if a parse error is encountered."); - argsParser.addFlag(P_TRAP_CACHE, 1, "Use the given directory as the TRAP cache."); - argsParser.addFlag( - P_TRAP_CACHE_BOUND, - 1, - "A (soft) upper limit on the size of the TRAP cache, " - + "in standard size units (e.g., 'g' for gigabytes)."); argsParser.addFlag(P_DEFAULT_ENCODING, 1, "The encoding to use; default is UTF-8."); argsParser.addFlag(P_TYPESCRIPT, 0, "Enable basic TypesScript support."); argsParser.addFlag( diff --git a/javascript/extractor/src/com/semmle/js/extractor/trapcache/DefaultTrapCache.java b/javascript/extractor/src/com/semmle/js/extractor/trapcache/DefaultTrapCache.java index 6caa29e8f63..c96be38c7d2 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/trapcache/DefaultTrapCache.java +++ b/javascript/extractor/src/com/semmle/js/extractor/trapcache/DefaultTrapCache.java @@ -26,9 +26,15 @@ public class DefaultTrapCache implements ITrapCache { */ private final String extractorVersion; - public DefaultTrapCache(String trapCache, Long sizeBound, String extractorVersion) { + /** + * Whether this cache supports write operations. + */ + private final boolean writeable; + + public DefaultTrapCache(String trapCache, Long sizeBound, String extractorVersion, boolean writeable) { this.trapCache = new File(trapCache); this.extractorVersion = extractorVersion; + this.writeable = writeable; try { initCache(sizeBound); } catch (ResourceError | SecurityException e) { @@ -135,6 +141,8 @@ public class DefaultTrapCache implements ITrapCache { digestor.write(type.toString()); digestor.write(config); digestor.write(source); - return new File(trapCache, digestor.getDigest() + ".trap.gz"); + File result = new File(trapCache, digestor.getDigest() + ".trap.gz"); + if (!writeable && !result.exists()) return null; // If the cache isn't writable, only return the file if it exists + return result; } } diff --git a/javascript/extractor/src/com/semmle/js/extractor/trapcache/ITrapCache.java b/javascript/extractor/src/com/semmle/js/extractor/trapcache/ITrapCache.java index 745e930e75e..fb7b553e14c 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/trapcache/ITrapCache.java +++ b/javascript/extractor/src/com/semmle/js/extractor/trapcache/ITrapCache.java @@ -1,7 +1,11 @@ package com.semmle.js.extractor.trapcache; +import static com.semmle.js.extractor.ExtractorOptionsUtil.readExtractorOption; + import com.semmle.js.extractor.ExtractorConfig; import com.semmle.js.extractor.FileExtractor; +import com.semmle.js.extractor.Main; +import com.semmle.util.exception.UserError; import java.io.File; /** Generic TRAP cache interface. */ @@ -18,4 +22,29 @@ public interface ITrapCache { * cached information), or does not yet exist (and should be populated by the extractor) */ public File lookup(String source, ExtractorConfig config, FileExtractor.FileType type); + + /** + * Build a TRAP cache as defined by the extractor options, which are read from the corresponding + * environment variables as defined in + * https://github.com/github/codeql-core/blob/main/design/spec/codeql-extractors.md + * + * @return a TRAP cache + */ + public static ITrapCache fromExtractorOptions() { + String trapCachePath = readExtractorOption("trap", "cache", "dir"); + if (trapCachePath != null) { + Long sizeBound = null; + String trapCacheBound = readExtractorOption("trap", "cache", "bound"); + if (trapCacheBound != null) { + sizeBound = DefaultTrapCache.asFileSize(trapCacheBound); + if (sizeBound == null) + throw new UserError("Invalid TRAP cache size bound: " + trapCacheBound); + } + boolean writeable = true; + String trapCacheWrite = readExtractorOption("trap", "cache", "write"); + if (trapCacheWrite != null) writeable = trapCacheWrite.equalsIgnoreCase("TRUE"); + return new DefaultTrapCache(trapCachePath, sizeBound, Main.EXTRACTOR_VERSION, writeable); + } + return new DummyTrapCache(); + } } From da4434033440399c0fc9e29bae74a0432accd887 Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Mon, 8 Aug 2022 12:22:41 +0200 Subject: [PATCH 486/505] formatting --- javascript/ql/test/library-tests/Arrays/TaintFlow.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/test/library-tests/Arrays/TaintFlow.ql b/javascript/ql/test/library-tests/Arrays/TaintFlow.ql index 694353873f2..cee2f294a34 100644 --- a/javascript/ql/test/library-tests/Arrays/TaintFlow.ql +++ b/javascript/ql/test/library-tests/Arrays/TaintFlow.ql @@ -1,7 +1,7 @@ import javascript class ArrayTaintFlowConfig extends TaintTracking::Configuration { - ArrayTaintFlowConfig() { this = "ArrayTaintFlowConfig" } + ArrayTaintFlowConfig() { this = "ArrayTaintFlowConfig" } override predicate isSource(DataFlow::Node source) { source.asExpr().getStringValue() = "source" } From 9268437a58b9a5abf4c7b0deb72aaf9549fc3b70 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 8 Aug 2022 12:42:04 +0200 Subject: [PATCH 487/505] Ruby: Generalize `SynthHashSplatParameterNode` to also work for synthesized methods --- .../dataflow/internal/DataFlowDispatch.qll | 7 +- .../dataflow/internal/DataFlowPrivate.qll | 82 ++--- .../dataflow/summaries/Summaries.expected | 316 +++++++++--------- .../dataflow/summaries/summaries.rb | 2 + 4 files changed, 213 insertions(+), 194 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowDispatch.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowDispatch.qll index 9bfc4f5c9b6..3ec315b5d9b 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowDispatch.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowDispatch.qll @@ -65,7 +65,12 @@ class DataFlowCallable extends TDataFlowCallable { string toString() { result = [this.asCallable().toString(), this.asLibraryCallable()] } /** Gets the location of this callable. */ - Location getLocation() { result = this.asCallable().getLocation() } + Location getLocation() { + result = this.asCallable().getLocation() + or + this instanceof TLibraryCallable and + result instanceof EmptyLocation + } } /** diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll index a9103e5b666..2386fd0bf6e 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll @@ -227,7 +227,9 @@ private module Cached { } or TSelfParameterNode(MethodBase m) or TBlockParameterNode(MethodBase m) or - TSynthHashSplatParameterNode(MethodBase m) { m.getAParameter() instanceof KeywordParameter } or + TSynthHashSplatParameterNode(DataFlowCallable c) { + isParameterNode(_, c, any(ParameterPosition p | p.isKeyword(_))) + } or TExprPostUpdateNode(CfgNodes::ExprCfgNode n) { n instanceof Argument or n = any(CfgNodes::ExprNodes::InstanceVariableAccessCfgNode v).getReceiver() @@ -477,10 +479,13 @@ private module ParameterNodes { abstract class ParameterNodeImpl extends NodeImpl { abstract Parameter getParameter(); - abstract predicate isSourceParameterOf(Callable c, ParameterPosition pos); + abstract predicate isParameterOf(DataFlowCallable c, ParameterPosition pos); - predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { - this.isSourceParameterOf(c.asCallable(), pos) + final predicate isSourceParameterOf(Callable c, ParameterPosition pos) { + exists(DataFlowCallable callable | + this.isParameterOf(callable, pos) and + c = callable.asCallable() + ) } } @@ -495,21 +500,23 @@ private module ParameterNodes { override Parameter getParameter() { result = parameter } - override predicate isSourceParameterOf(Callable c, ParameterPosition pos) { - exists(int i | pos.isPositional(i) and c.getParameter(i) = parameter | - parameter instanceof SimpleParameter - or - parameter instanceof OptionalParameter - ) - or - parameter = - any(KeywordParameter kp | - c.getAParameter() = kp and - pos.isKeyword(kp.getName()) + override predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { + exists(Callable callable | callable = c.asCallable() | + exists(int i | pos.isPositional(i) and callable.getParameter(i) = parameter | + parameter instanceof SimpleParameter + or + parameter instanceof OptionalParameter ) - or - parameter = c.getAParameter().(HashSplatParameter) and - pos.isHashSplat() + or + parameter = + any(KeywordParameter kp | + callable.getAParameter() = kp and + pos.isKeyword(kp.getName()) + ) + or + parameter = callable.getAParameter().(HashSplatParameter) and + pos.isHashSplat() + ) } override CfgScope getCfgScope() { result = parameter.getCallable() } @@ -532,8 +539,8 @@ private module ParameterNodes { override Parameter getParameter() { none() } - override predicate isSourceParameterOf(Callable c, ParameterPosition pos) { - method = c and pos.isSelf() + override predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { + method = c.asCallable() and pos.isSelf() } override CfgScope getCfgScope() { result = method } @@ -558,8 +565,8 @@ private module ParameterNodes { result = method.getAParameter() and result instanceof BlockParameter } - override predicate isSourceParameterOf(Callable c, ParameterPosition pos) { - c = method and pos.isBlock() + override predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { + c.asCallable() = method and pos.isBlock() } override CfgScope getCfgScope() { result = method } @@ -612,37 +619,36 @@ private module ParameterNodes { * collapsed anyway. */ class SynthHashSplatParameterNode extends ParameterNodeImpl, TSynthHashSplatParameterNode { - private MethodBase method; + private DataFlowCallable callable; - SynthHashSplatParameterNode() { this = TSynthHashSplatParameterNode(method) } - - final Callable getMethod() { result = method } + SynthHashSplatParameterNode() { this = TSynthHashSplatParameterNode(callable) } /** * Gets a keyword parameter that will be the result of reading `c` out of this * synthesized node. */ - NormalParameterNode getAKeywordParameter(ContentSet c) { - exists(KeywordParameter p | - p = result.getParameter() and - p = method.getAParameter() + ParameterNode getAKeywordParameter(ContentSet c) { + exists(string name | + isParameterNode(result, callable, any(ParameterPosition p | p.isKeyword(name))) | - c = getKeywordContent(p.getName()) or + c = getKeywordContent(name) or c.isSingleton(TUnknownElementContent()) ) } - override Parameter getParameter() { none() } + final override Parameter getParameter() { none() } - override predicate isSourceParameterOf(Callable c, ParameterPosition pos) { - c = method and pos.isHashSplat() + final override predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { + c = callable and pos.isHashSplat() } - override CfgScope getCfgScope() { result = method } + final override CfgScope getCfgScope() { result = callable.asCallable() } - override Location getLocationImpl() { result = method.getLocation() } + final override DataFlowCallable getEnclosingCallable() { result = callable } - override string toStringImpl() { result = "**kwargs" } + final override Location getLocationImpl() { result = callable.getLocation() } + + final override string toStringImpl() { result = "**kwargs" } } /** A parameter for a library callable with a flow summary. */ @@ -654,8 +660,6 @@ private module ParameterNodes { override Parameter getParameter() { none() } - override predicate isSourceParameterOf(Callable c, ParameterPosition pos) { none() } - override predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { sc = c.asLibraryCallable() and pos = pos_ } diff --git a/ruby/ql/test/library-tests/dataflow/summaries/Summaries.expected b/ruby/ql/test/library-tests/dataflow/summaries/Summaries.expected index 595ec706db6..1669a6b07fa 100644 --- a/ruby/ql/test/library-tests/dataflow/summaries/Summaries.expected +++ b/ruby/ql/test/library-tests/dataflow/summaries/Summaries.expected @@ -19,19 +19,19 @@ edges | summaries.rb:1:11:1:36 | call to identity : | summaries.rb:37:36:37:42 | tainted | | summaries.rb:1:11:1:36 | call to identity : | summaries.rb:37:36:37:42 | tainted | | summaries.rb:1:11:1:36 | call to identity : | summaries.rb:51:24:51:30 | tainted : | -| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:54:22:54:28 | tainted : | -| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:55:17:55:23 | tainted : | -| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:57:27:57:33 | tainted : | -| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:61:32:61:38 | tainted : | -| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:63:23:63:29 | tainted : | -| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:102:16:102:22 | tainted : | -| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:108:14:108:20 | tainted : | -| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:111:16:111:22 | tainted | -| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:111:16:111:22 | tainted | -| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:112:21:112:27 | tainted | -| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:112:21:112:27 | tainted | -| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:115:26:115:32 | tainted | -| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:115:26:115:32 | tainted | +| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:56:22:56:28 | tainted : | +| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:57:17:57:23 | tainted : | +| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:59:27:59:33 | tainted : | +| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:63:32:63:38 | tainted : | +| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:65:23:65:29 | tainted : | +| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:104:16:104:22 | tainted : | +| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:110:14:110:20 | tainted : | +| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:113:16:113:22 | tainted | +| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:113:16:113:22 | tainted | +| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:114:21:114:27 | tainted | +| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:114:21:114:27 | tainted | +| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:117:26:117:32 | tainted | +| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:117:26:117:32 | tainted | | summaries.rb:1:20:1:36 | call to source : | summaries.rb:1:11:1:36 | call to identity : | | summaries.rb:1:20:1:36 | call to source : | summaries.rb:1:11:1:36 | call to identity : | | summaries.rb:4:12:7:3 | call to apply_block : | summaries.rb:9:6:9:13 | tainted2 | @@ -64,55 +64,58 @@ edges | summaries.rb:44:8:44:8 | t : | summaries.rb:44:8:44:27 | call to matchedByNameRcv | | summaries.rb:48:24:48:41 | call to source : | summaries.rb:48:8:48:42 | call to preserveTaint | | summaries.rb:51:24:51:30 | tainted : | summaries.rb:51:6:51:31 | call to namedArg | -| summaries.rb:54:22:54:28 | tainted : | summaries.rb:54:6:54:29 | call to anyArg | -| summaries.rb:55:17:55:23 | tainted : | summaries.rb:55:6:55:24 | call to anyArg | -| summaries.rb:57:27:57:33 | tainted : | summaries.rb:57:6:57:34 | call to anyNamedArg | -| summaries.rb:61:32:61:38 | tainted : | summaries.rb:61:6:61:39 | call to anyPositionFromOne | -| summaries.rb:63:23:63:29 | tainted : | summaries.rb:63:40:63:40 | x : | -| summaries.rb:63:40:63:40 | x : | summaries.rb:64:8:64:8 | x | -| summaries.rb:71:24:71:53 | call to source : | summaries.rb:71:8:71:54 | call to preserveTaint | -| summaries.rb:74:26:74:56 | call to source : | summaries.rb:74:8:74:57 | call to preserveTaint | -| summaries.rb:77:15:77:29 | call to source : | summaries.rb:79:6:79:6 | a [element 1] : | -| summaries.rb:77:15:77:29 | call to source : | summaries.rb:79:6:79:6 | a [element 1] : | -| summaries.rb:77:15:77:29 | call to source : | summaries.rb:81:5:81:5 | a [element 1] : | -| summaries.rb:77:15:77:29 | call to source : | summaries.rb:81:5:81:5 | a [element 1] : | -| summaries.rb:77:32:77:46 | call to source : | summaries.rb:80:6:80:6 | a [element 2] : | -| summaries.rb:77:32:77:46 | call to source : | summaries.rb:80:6:80:6 | a [element 2] : | -| summaries.rb:77:32:77:46 | call to source : | summaries.rb:85:1:85:1 | a [element 2] : | -| summaries.rb:77:32:77:46 | call to source : | summaries.rb:85:1:85:1 | a [element 2] : | -| summaries.rb:79:6:79:6 | a [element 1] : | summaries.rb:79:6:79:9 | ...[...] | -| summaries.rb:79:6:79:6 | a [element 1] : | summaries.rb:79:6:79:9 | ...[...] | -| summaries.rb:80:6:80:6 | a [element 2] : | summaries.rb:80:6:80:9 | ...[...] | -| summaries.rb:80:6:80:6 | a [element 2] : | summaries.rb:80:6:80:9 | ...[...] | -| summaries.rb:81:5:81:5 | a [element 1] : | summaries.rb:81:5:81:22 | call to withElementOne [element 1] : | -| summaries.rb:81:5:81:5 | a [element 1] : | summaries.rb:81:5:81:22 | call to withElementOne [element 1] : | -| summaries.rb:81:5:81:22 | call to withElementOne [element 1] : | summaries.rb:83:6:83:6 | b [element 1] : | -| summaries.rb:81:5:81:22 | call to withElementOne [element 1] : | summaries.rb:83:6:83:6 | b [element 1] : | -| summaries.rb:83:6:83:6 | b [element 1] : | summaries.rb:83:6:83:9 | ...[...] | -| summaries.rb:83:6:83:6 | b [element 1] : | summaries.rb:83:6:83:9 | ...[...] | -| summaries.rb:85:1:85:1 | [post] a [element 2] : | summaries.rb:88:6:88:6 | a [element 2] : | -| summaries.rb:85:1:85:1 | [post] a [element 2] : | summaries.rb:88:6:88:6 | a [element 2] : | -| summaries.rb:85:1:85:1 | a [element 2] : | summaries.rb:85:1:85:1 | [post] a [element 2] : | -| summaries.rb:85:1:85:1 | a [element 2] : | summaries.rb:85:1:85:1 | [post] a [element 2] : | -| summaries.rb:88:6:88:6 | a [element 2] : | summaries.rb:88:6:88:9 | ...[...] | -| summaries.rb:88:6:88:6 | a [element 2] : | summaries.rb:88:6:88:9 | ...[...] | -| summaries.rb:91:1:91:1 | [post] x [@value] : | summaries.rb:92:6:92:6 | x [@value] : | -| summaries.rb:91:1:91:1 | [post] x [@value] : | summaries.rb:92:6:92:6 | x [@value] : | -| summaries.rb:91:13:91:26 | call to source : | summaries.rb:91:1:91:1 | [post] x [@value] : | -| summaries.rb:91:13:91:26 | call to source : | summaries.rb:91:1:91:1 | [post] x [@value] : | -| summaries.rb:92:6:92:6 | x [@value] : | summaries.rb:92:6:92:16 | call to get_value | -| summaries.rb:92:6:92:6 | x [@value] : | summaries.rb:92:6:92:16 | call to get_value | -| summaries.rb:102:16:102:22 | [post] tainted : | summaries.rb:108:14:108:20 | tainted : | -| summaries.rb:102:16:102:22 | [post] tainted : | summaries.rb:111:16:111:22 | tainted | -| summaries.rb:102:16:102:22 | [post] tainted : | summaries.rb:112:21:112:27 | tainted | -| summaries.rb:102:16:102:22 | [post] tainted : | summaries.rb:115:26:115:32 | tainted | -| summaries.rb:102:16:102:22 | tainted : | summaries.rb:102:16:102:22 | [post] tainted : | -| summaries.rb:102:16:102:22 | tainted : | summaries.rb:102:25:102:25 | [post] y : | -| summaries.rb:102:16:102:22 | tainted : | summaries.rb:102:33:102:33 | [post] z : | -| summaries.rb:102:25:102:25 | [post] y : | summaries.rb:104:6:104:6 | y | -| summaries.rb:102:33:102:33 | [post] z : | summaries.rb:105:6:105:6 | z | -| summaries.rb:108:1:108:1 | [post] x : | summaries.rb:109:6:109:6 | x | -| summaries.rb:108:14:108:20 | tainted : | summaries.rb:108:1:108:1 | [post] x : | +| summaries.rb:53:15:53:31 | call to source : | summaries.rb:54:21:54:24 | args [element :foo] : | +| summaries.rb:54:19:54:24 | ** ... [element :foo] : | summaries.rb:54:6:54:25 | call to namedArg | +| summaries.rb:54:21:54:24 | args [element :foo] : | summaries.rb:54:19:54:24 | ** ... [element :foo] : | +| summaries.rb:56:22:56:28 | tainted : | summaries.rb:56:6:56:29 | call to anyArg | +| summaries.rb:57:17:57:23 | tainted : | summaries.rb:57:6:57:24 | call to anyArg | +| summaries.rb:59:27:59:33 | tainted : | summaries.rb:59:6:59:34 | call to anyNamedArg | +| summaries.rb:63:32:63:38 | tainted : | summaries.rb:63:6:63:39 | call to anyPositionFromOne | +| summaries.rb:65:23:65:29 | tainted : | summaries.rb:65:40:65:40 | x : | +| summaries.rb:65:40:65:40 | x : | summaries.rb:66:8:66:8 | x | +| summaries.rb:73:24:73:53 | call to source : | summaries.rb:73:8:73:54 | call to preserveTaint | +| summaries.rb:76:26:76:56 | call to source : | summaries.rb:76:8:76:57 | call to preserveTaint | +| summaries.rb:79:15:79:29 | call to source : | summaries.rb:81:6:81:6 | a [element 1] : | +| summaries.rb:79:15:79:29 | call to source : | summaries.rb:81:6:81:6 | a [element 1] : | +| summaries.rb:79:15:79:29 | call to source : | summaries.rb:83:5:83:5 | a [element 1] : | +| summaries.rb:79:15:79:29 | call to source : | summaries.rb:83:5:83:5 | a [element 1] : | +| summaries.rb:79:32:79:46 | call to source : | summaries.rb:82:6:82:6 | a [element 2] : | +| summaries.rb:79:32:79:46 | call to source : | summaries.rb:82:6:82:6 | a [element 2] : | +| summaries.rb:79:32:79:46 | call to source : | summaries.rb:87:1:87:1 | a [element 2] : | +| summaries.rb:79:32:79:46 | call to source : | summaries.rb:87:1:87:1 | a [element 2] : | +| summaries.rb:81:6:81:6 | a [element 1] : | summaries.rb:81:6:81:9 | ...[...] | +| summaries.rb:81:6:81:6 | a [element 1] : | summaries.rb:81:6:81:9 | ...[...] | +| summaries.rb:82:6:82:6 | a [element 2] : | summaries.rb:82:6:82:9 | ...[...] | +| summaries.rb:82:6:82:6 | a [element 2] : | summaries.rb:82:6:82:9 | ...[...] | +| summaries.rb:83:5:83:5 | a [element 1] : | summaries.rb:83:5:83:22 | call to withElementOne [element 1] : | +| summaries.rb:83:5:83:5 | a [element 1] : | summaries.rb:83:5:83:22 | call to withElementOne [element 1] : | +| summaries.rb:83:5:83:22 | call to withElementOne [element 1] : | summaries.rb:85:6:85:6 | b [element 1] : | +| summaries.rb:83:5:83:22 | call to withElementOne [element 1] : | summaries.rb:85:6:85:6 | b [element 1] : | +| summaries.rb:85:6:85:6 | b [element 1] : | summaries.rb:85:6:85:9 | ...[...] | +| summaries.rb:85:6:85:6 | b [element 1] : | summaries.rb:85:6:85:9 | ...[...] | +| summaries.rb:87:1:87:1 | [post] a [element 2] : | summaries.rb:90:6:90:6 | a [element 2] : | +| summaries.rb:87:1:87:1 | [post] a [element 2] : | summaries.rb:90:6:90:6 | a [element 2] : | +| summaries.rb:87:1:87:1 | a [element 2] : | summaries.rb:87:1:87:1 | [post] a [element 2] : | +| summaries.rb:87:1:87:1 | a [element 2] : | summaries.rb:87:1:87:1 | [post] a [element 2] : | +| summaries.rb:90:6:90:6 | a [element 2] : | summaries.rb:90:6:90:9 | ...[...] | +| summaries.rb:90:6:90:6 | a [element 2] : | summaries.rb:90:6:90:9 | ...[...] | +| summaries.rb:93:1:93:1 | [post] x [@value] : | summaries.rb:94:6:94:6 | x [@value] : | +| summaries.rb:93:1:93:1 | [post] x [@value] : | summaries.rb:94:6:94:6 | x [@value] : | +| summaries.rb:93:13:93:26 | call to source : | summaries.rb:93:1:93:1 | [post] x [@value] : | +| summaries.rb:93:13:93:26 | call to source : | summaries.rb:93:1:93:1 | [post] x [@value] : | +| summaries.rb:94:6:94:6 | x [@value] : | summaries.rb:94:6:94:16 | call to get_value | +| summaries.rb:94:6:94:6 | x [@value] : | summaries.rb:94:6:94:16 | call to get_value | +| summaries.rb:104:16:104:22 | [post] tainted : | summaries.rb:110:14:110:20 | tainted : | +| summaries.rb:104:16:104:22 | [post] tainted : | summaries.rb:113:16:113:22 | tainted | +| summaries.rb:104:16:104:22 | [post] tainted : | summaries.rb:114:21:114:27 | tainted | +| summaries.rb:104:16:104:22 | [post] tainted : | summaries.rb:117:26:117:32 | tainted | +| summaries.rb:104:16:104:22 | tainted : | summaries.rb:104:16:104:22 | [post] tainted : | +| summaries.rb:104:16:104:22 | tainted : | summaries.rb:104:25:104:25 | [post] y : | +| summaries.rb:104:16:104:22 | tainted : | summaries.rb:104:33:104:33 | [post] z : | +| summaries.rb:104:25:104:25 | [post] y : | summaries.rb:106:6:106:6 | y | +| summaries.rb:104:33:104:33 | [post] z : | summaries.rb:107:6:107:6 | z | +| summaries.rb:110:1:110:1 | [post] x : | summaries.rb:111:6:111:6 | x | +| summaries.rb:110:14:110:20 | tainted : | summaries.rb:110:1:110:1 | [post] x : | nodes | summaries.rb:1:11:1:36 | call to identity : | semmle.label | call to identity : | | summaries.rb:1:11:1:36 | call to identity : | semmle.label | call to identity : | @@ -169,72 +172,76 @@ nodes | summaries.rb:48:24:48:41 | call to source : | semmle.label | call to source : | | summaries.rb:51:6:51:31 | call to namedArg | semmle.label | call to namedArg | | summaries.rb:51:24:51:30 | tainted : | semmle.label | tainted : | -| summaries.rb:54:6:54:29 | call to anyArg | semmle.label | call to anyArg | -| summaries.rb:54:22:54:28 | tainted : | semmle.label | tainted : | -| summaries.rb:55:6:55:24 | call to anyArg | semmle.label | call to anyArg | -| summaries.rb:55:17:55:23 | tainted : | semmle.label | tainted : | -| summaries.rb:57:6:57:34 | call to anyNamedArg | semmle.label | call to anyNamedArg | -| summaries.rb:57:27:57:33 | tainted : | semmle.label | tainted : | -| summaries.rb:61:6:61:39 | call to anyPositionFromOne | semmle.label | call to anyPositionFromOne | -| summaries.rb:61:32:61:38 | tainted : | semmle.label | tainted : | -| summaries.rb:63:23:63:29 | tainted : | semmle.label | tainted : | -| summaries.rb:63:40:63:40 | x : | semmle.label | x : | -| summaries.rb:64:8:64:8 | x | semmle.label | x | -| summaries.rb:71:8:71:54 | call to preserveTaint | semmle.label | call to preserveTaint | -| summaries.rb:71:24:71:53 | call to source : | semmle.label | call to source : | -| summaries.rb:74:8:74:57 | call to preserveTaint | semmle.label | call to preserveTaint | -| summaries.rb:74:26:74:56 | call to source : | semmle.label | call to source : | -| summaries.rb:77:15:77:29 | call to source : | semmle.label | call to source : | -| summaries.rb:77:15:77:29 | call to source : | semmle.label | call to source : | -| summaries.rb:77:32:77:46 | call to source : | semmle.label | call to source : | -| summaries.rb:77:32:77:46 | call to source : | semmle.label | call to source : | -| summaries.rb:79:6:79:6 | a [element 1] : | semmle.label | a [element 1] : | -| summaries.rb:79:6:79:6 | a [element 1] : | semmle.label | a [element 1] : | -| summaries.rb:79:6:79:9 | ...[...] | semmle.label | ...[...] | -| summaries.rb:79:6:79:9 | ...[...] | semmle.label | ...[...] | -| summaries.rb:80:6:80:6 | a [element 2] : | semmle.label | a [element 2] : | -| summaries.rb:80:6:80:6 | a [element 2] : | semmle.label | a [element 2] : | -| summaries.rb:80:6:80:9 | ...[...] | semmle.label | ...[...] | -| summaries.rb:80:6:80:9 | ...[...] | semmle.label | ...[...] | -| summaries.rb:81:5:81:5 | a [element 1] : | semmle.label | a [element 1] : | -| summaries.rb:81:5:81:5 | a [element 1] : | semmle.label | a [element 1] : | -| summaries.rb:81:5:81:22 | call to withElementOne [element 1] : | semmle.label | call to withElementOne [element 1] : | -| summaries.rb:81:5:81:22 | call to withElementOne [element 1] : | semmle.label | call to withElementOne [element 1] : | -| summaries.rb:83:6:83:6 | b [element 1] : | semmle.label | b [element 1] : | -| summaries.rb:83:6:83:6 | b [element 1] : | semmle.label | b [element 1] : | -| summaries.rb:83:6:83:9 | ...[...] | semmle.label | ...[...] | -| summaries.rb:83:6:83:9 | ...[...] | semmle.label | ...[...] | -| summaries.rb:85:1:85:1 | [post] a [element 2] : | semmle.label | [post] a [element 2] : | -| summaries.rb:85:1:85:1 | [post] a [element 2] : | semmle.label | [post] a [element 2] : | -| summaries.rb:85:1:85:1 | a [element 2] : | semmle.label | a [element 2] : | -| summaries.rb:85:1:85:1 | a [element 2] : | semmle.label | a [element 2] : | -| summaries.rb:88:6:88:6 | a [element 2] : | semmle.label | a [element 2] : | -| summaries.rb:88:6:88:6 | a [element 2] : | semmle.label | a [element 2] : | -| summaries.rb:88:6:88:9 | ...[...] | semmle.label | ...[...] | -| summaries.rb:88:6:88:9 | ...[...] | semmle.label | ...[...] | -| summaries.rb:91:1:91:1 | [post] x [@value] : | semmle.label | [post] x [@value] : | -| summaries.rb:91:1:91:1 | [post] x [@value] : | semmle.label | [post] x [@value] : | -| summaries.rb:91:13:91:26 | call to source : | semmle.label | call to source : | -| summaries.rb:91:13:91:26 | call to source : | semmle.label | call to source : | -| summaries.rb:92:6:92:6 | x [@value] : | semmle.label | x [@value] : | -| summaries.rb:92:6:92:6 | x [@value] : | semmle.label | x [@value] : | -| summaries.rb:92:6:92:16 | call to get_value | semmle.label | call to get_value | -| summaries.rb:92:6:92:16 | call to get_value | semmle.label | call to get_value | -| summaries.rb:102:16:102:22 | [post] tainted : | semmle.label | [post] tainted : | -| summaries.rb:102:16:102:22 | tainted : | semmle.label | tainted : | -| summaries.rb:102:25:102:25 | [post] y : | semmle.label | [post] y : | -| summaries.rb:102:33:102:33 | [post] z : | semmle.label | [post] z : | -| summaries.rb:104:6:104:6 | y | semmle.label | y | -| summaries.rb:105:6:105:6 | z | semmle.label | z | -| summaries.rb:108:1:108:1 | [post] x : | semmle.label | [post] x : | -| summaries.rb:108:14:108:20 | tainted : | semmle.label | tainted : | -| summaries.rb:109:6:109:6 | x | semmle.label | x | -| summaries.rb:111:16:111:22 | tainted | semmle.label | tainted | -| summaries.rb:111:16:111:22 | tainted | semmle.label | tainted | -| summaries.rb:112:21:112:27 | tainted | semmle.label | tainted | -| summaries.rb:112:21:112:27 | tainted | semmle.label | tainted | -| summaries.rb:115:26:115:32 | tainted | semmle.label | tainted | -| summaries.rb:115:26:115:32 | tainted | semmle.label | tainted | +| summaries.rb:53:15:53:31 | call to source : | semmle.label | call to source : | +| summaries.rb:54:6:54:25 | call to namedArg | semmle.label | call to namedArg | +| summaries.rb:54:19:54:24 | ** ... [element :foo] : | semmle.label | ** ... [element :foo] : | +| summaries.rb:54:21:54:24 | args [element :foo] : | semmle.label | args [element :foo] : | +| summaries.rb:56:6:56:29 | call to anyArg | semmle.label | call to anyArg | +| summaries.rb:56:22:56:28 | tainted : | semmle.label | tainted : | +| summaries.rb:57:6:57:24 | call to anyArg | semmle.label | call to anyArg | +| summaries.rb:57:17:57:23 | tainted : | semmle.label | tainted : | +| summaries.rb:59:6:59:34 | call to anyNamedArg | semmle.label | call to anyNamedArg | +| summaries.rb:59:27:59:33 | tainted : | semmle.label | tainted : | +| summaries.rb:63:6:63:39 | call to anyPositionFromOne | semmle.label | call to anyPositionFromOne | +| summaries.rb:63:32:63:38 | tainted : | semmle.label | tainted : | +| summaries.rb:65:23:65:29 | tainted : | semmle.label | tainted : | +| summaries.rb:65:40:65:40 | x : | semmle.label | x : | +| summaries.rb:66:8:66:8 | x | semmle.label | x | +| summaries.rb:73:8:73:54 | call to preserveTaint | semmle.label | call to preserveTaint | +| summaries.rb:73:24:73:53 | call to source : | semmle.label | call to source : | +| summaries.rb:76:8:76:57 | call to preserveTaint | semmle.label | call to preserveTaint | +| summaries.rb:76:26:76:56 | call to source : | semmle.label | call to source : | +| summaries.rb:79:15:79:29 | call to source : | semmle.label | call to source : | +| summaries.rb:79:15:79:29 | call to source : | semmle.label | call to source : | +| summaries.rb:79:32:79:46 | call to source : | semmle.label | call to source : | +| summaries.rb:79:32:79:46 | call to source : | semmle.label | call to source : | +| summaries.rb:81:6:81:6 | a [element 1] : | semmle.label | a [element 1] : | +| summaries.rb:81:6:81:6 | a [element 1] : | semmle.label | a [element 1] : | +| summaries.rb:81:6:81:9 | ...[...] | semmle.label | ...[...] | +| summaries.rb:81:6:81:9 | ...[...] | semmle.label | ...[...] | +| summaries.rb:82:6:82:6 | a [element 2] : | semmle.label | a [element 2] : | +| summaries.rb:82:6:82:6 | a [element 2] : | semmle.label | a [element 2] : | +| summaries.rb:82:6:82:9 | ...[...] | semmle.label | ...[...] | +| summaries.rb:82:6:82:9 | ...[...] | semmle.label | ...[...] | +| summaries.rb:83:5:83:5 | a [element 1] : | semmle.label | a [element 1] : | +| summaries.rb:83:5:83:5 | a [element 1] : | semmle.label | a [element 1] : | +| summaries.rb:83:5:83:22 | call to withElementOne [element 1] : | semmle.label | call to withElementOne [element 1] : | +| summaries.rb:83:5:83:22 | call to withElementOne [element 1] : | semmle.label | call to withElementOne [element 1] : | +| summaries.rb:85:6:85:6 | b [element 1] : | semmle.label | b [element 1] : | +| summaries.rb:85:6:85:6 | b [element 1] : | semmle.label | b [element 1] : | +| summaries.rb:85:6:85:9 | ...[...] | semmle.label | ...[...] | +| summaries.rb:85:6:85:9 | ...[...] | semmle.label | ...[...] | +| summaries.rb:87:1:87:1 | [post] a [element 2] : | semmle.label | [post] a [element 2] : | +| summaries.rb:87:1:87:1 | [post] a [element 2] : | semmle.label | [post] a [element 2] : | +| summaries.rb:87:1:87:1 | a [element 2] : | semmle.label | a [element 2] : | +| summaries.rb:87:1:87:1 | a [element 2] : | semmle.label | a [element 2] : | +| summaries.rb:90:6:90:6 | a [element 2] : | semmle.label | a [element 2] : | +| summaries.rb:90:6:90:6 | a [element 2] : | semmle.label | a [element 2] : | +| summaries.rb:90:6:90:9 | ...[...] | semmle.label | ...[...] | +| summaries.rb:90:6:90:9 | ...[...] | semmle.label | ...[...] | +| summaries.rb:93:1:93:1 | [post] x [@value] : | semmle.label | [post] x [@value] : | +| summaries.rb:93:1:93:1 | [post] x [@value] : | semmle.label | [post] x [@value] : | +| summaries.rb:93:13:93:26 | call to source : | semmle.label | call to source : | +| summaries.rb:93:13:93:26 | call to source : | semmle.label | call to source : | +| summaries.rb:94:6:94:6 | x [@value] : | semmle.label | x [@value] : | +| summaries.rb:94:6:94:6 | x [@value] : | semmle.label | x [@value] : | +| summaries.rb:94:6:94:16 | call to get_value | semmle.label | call to get_value | +| summaries.rb:94:6:94:16 | call to get_value | semmle.label | call to get_value | +| summaries.rb:104:16:104:22 | [post] tainted : | semmle.label | [post] tainted : | +| summaries.rb:104:16:104:22 | tainted : | semmle.label | tainted : | +| summaries.rb:104:25:104:25 | [post] y : | semmle.label | [post] y : | +| summaries.rb:104:33:104:33 | [post] z : | semmle.label | [post] z : | +| summaries.rb:106:6:106:6 | y | semmle.label | y | +| summaries.rb:107:6:107:6 | z | semmle.label | z | +| summaries.rb:110:1:110:1 | [post] x : | semmle.label | [post] x : | +| summaries.rb:110:14:110:20 | tainted : | semmle.label | tainted : | +| summaries.rb:111:6:111:6 | x | semmle.label | x | +| summaries.rb:113:16:113:22 | tainted | semmle.label | tainted | +| summaries.rb:113:16:113:22 | tainted | semmle.label | tainted | +| summaries.rb:114:21:114:27 | tainted | semmle.label | tainted | +| summaries.rb:114:21:114:27 | tainted | semmle.label | tainted | +| summaries.rb:117:26:117:32 | tainted | semmle.label | tainted | +| summaries.rb:117:26:117:32 | tainted | semmle.label | tainted | subpaths invalidSpecComponent #select @@ -265,32 +272,33 @@ invalidSpecComponent | summaries.rb:44:8:44:27 | call to matchedByNameRcv | summaries.rb:40:7:40:17 | call to source : | summaries.rb:44:8:44:27 | call to matchedByNameRcv | $@ | summaries.rb:40:7:40:17 | call to source : | call to source : | | summaries.rb:48:8:48:42 | call to preserveTaint | summaries.rb:48:24:48:41 | call to source : | summaries.rb:48:8:48:42 | call to preserveTaint | $@ | summaries.rb:48:24:48:41 | call to source : | call to source : | | summaries.rb:51:6:51:31 | call to namedArg | summaries.rb:1:20:1:36 | call to source : | summaries.rb:51:6:51:31 | call to namedArg | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | -| summaries.rb:54:6:54:29 | call to anyArg | summaries.rb:1:20:1:36 | call to source : | summaries.rb:54:6:54:29 | call to anyArg | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | -| summaries.rb:55:6:55:24 | call to anyArg | summaries.rb:1:20:1:36 | call to source : | summaries.rb:55:6:55:24 | call to anyArg | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | -| summaries.rb:57:6:57:34 | call to anyNamedArg | summaries.rb:1:20:1:36 | call to source : | summaries.rb:57:6:57:34 | call to anyNamedArg | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | -| summaries.rb:61:6:61:39 | call to anyPositionFromOne | summaries.rb:1:20:1:36 | call to source : | summaries.rb:61:6:61:39 | call to anyPositionFromOne | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | -| summaries.rb:64:8:64:8 | x | summaries.rb:1:20:1:36 | call to source : | summaries.rb:64:8:64:8 | x | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | -| summaries.rb:71:8:71:54 | call to preserveTaint | summaries.rb:71:24:71:53 | call to source : | summaries.rb:71:8:71:54 | call to preserveTaint | $@ | summaries.rb:71:24:71:53 | call to source : | call to source : | -| summaries.rb:74:8:74:57 | call to preserveTaint | summaries.rb:74:26:74:56 | call to source : | summaries.rb:74:8:74:57 | call to preserveTaint | $@ | summaries.rb:74:26:74:56 | call to source : | call to source : | -| summaries.rb:79:6:79:9 | ...[...] | summaries.rb:77:15:77:29 | call to source : | summaries.rb:79:6:79:9 | ...[...] | $@ | summaries.rb:77:15:77:29 | call to source : | call to source : | -| summaries.rb:79:6:79:9 | ...[...] | summaries.rb:77:15:77:29 | call to source : | summaries.rb:79:6:79:9 | ...[...] | $@ | summaries.rb:77:15:77:29 | call to source : | call to source : | -| summaries.rb:80:6:80:9 | ...[...] | summaries.rb:77:32:77:46 | call to source : | summaries.rb:80:6:80:9 | ...[...] | $@ | summaries.rb:77:32:77:46 | call to source : | call to source : | -| summaries.rb:80:6:80:9 | ...[...] | summaries.rb:77:32:77:46 | call to source : | summaries.rb:80:6:80:9 | ...[...] | $@ | summaries.rb:77:32:77:46 | call to source : | call to source : | -| summaries.rb:83:6:83:9 | ...[...] | summaries.rb:77:15:77:29 | call to source : | summaries.rb:83:6:83:9 | ...[...] | $@ | summaries.rb:77:15:77:29 | call to source : | call to source : | -| summaries.rb:83:6:83:9 | ...[...] | summaries.rb:77:15:77:29 | call to source : | summaries.rb:83:6:83:9 | ...[...] | $@ | summaries.rb:77:15:77:29 | call to source : | call to source : | -| summaries.rb:88:6:88:9 | ...[...] | summaries.rb:77:32:77:46 | call to source : | summaries.rb:88:6:88:9 | ...[...] | $@ | summaries.rb:77:32:77:46 | call to source : | call to source : | -| summaries.rb:88:6:88:9 | ...[...] | summaries.rb:77:32:77:46 | call to source : | summaries.rb:88:6:88:9 | ...[...] | $@ | summaries.rb:77:32:77:46 | call to source : | call to source : | -| summaries.rb:92:6:92:16 | call to get_value | summaries.rb:91:13:91:26 | call to source : | summaries.rb:92:6:92:16 | call to get_value | $@ | summaries.rb:91:13:91:26 | call to source : | call to source : | -| summaries.rb:92:6:92:16 | call to get_value | summaries.rb:91:13:91:26 | call to source : | summaries.rb:92:6:92:16 | call to get_value | $@ | summaries.rb:91:13:91:26 | call to source : | call to source : | -| summaries.rb:104:6:104:6 | y | summaries.rb:1:20:1:36 | call to source : | summaries.rb:104:6:104:6 | y | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | -| summaries.rb:105:6:105:6 | z | summaries.rb:1:20:1:36 | call to source : | summaries.rb:105:6:105:6 | z | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | -| summaries.rb:109:6:109:6 | x | summaries.rb:1:20:1:36 | call to source : | summaries.rb:109:6:109:6 | x | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | -| summaries.rb:111:16:111:22 | tainted | summaries.rb:1:20:1:36 | call to source : | summaries.rb:111:16:111:22 | tainted | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | -| summaries.rb:111:16:111:22 | tainted | summaries.rb:1:20:1:36 | call to source : | summaries.rb:111:16:111:22 | tainted | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | -| summaries.rb:112:21:112:27 | tainted | summaries.rb:1:20:1:36 | call to source : | summaries.rb:112:21:112:27 | tainted | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | -| summaries.rb:112:21:112:27 | tainted | summaries.rb:1:20:1:36 | call to source : | summaries.rb:112:21:112:27 | tainted | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | -| summaries.rb:115:26:115:32 | tainted | summaries.rb:1:20:1:36 | call to source : | summaries.rb:115:26:115:32 | tainted | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | -| summaries.rb:115:26:115:32 | tainted | summaries.rb:1:20:1:36 | call to source : | summaries.rb:115:26:115:32 | tainted | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | +| summaries.rb:54:6:54:25 | call to namedArg | summaries.rb:53:15:53:31 | call to source : | summaries.rb:54:6:54:25 | call to namedArg | $@ | summaries.rb:53:15:53:31 | call to source : | call to source : | +| summaries.rb:56:6:56:29 | call to anyArg | summaries.rb:1:20:1:36 | call to source : | summaries.rb:56:6:56:29 | call to anyArg | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | +| summaries.rb:57:6:57:24 | call to anyArg | summaries.rb:1:20:1:36 | call to source : | summaries.rb:57:6:57:24 | call to anyArg | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | +| summaries.rb:59:6:59:34 | call to anyNamedArg | summaries.rb:1:20:1:36 | call to source : | summaries.rb:59:6:59:34 | call to anyNamedArg | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | +| summaries.rb:63:6:63:39 | call to anyPositionFromOne | summaries.rb:1:20:1:36 | call to source : | summaries.rb:63:6:63:39 | call to anyPositionFromOne | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | +| summaries.rb:66:8:66:8 | x | summaries.rb:1:20:1:36 | call to source : | summaries.rb:66:8:66:8 | x | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | +| summaries.rb:73:8:73:54 | call to preserveTaint | summaries.rb:73:24:73:53 | call to source : | summaries.rb:73:8:73:54 | call to preserveTaint | $@ | summaries.rb:73:24:73:53 | call to source : | call to source : | +| summaries.rb:76:8:76:57 | call to preserveTaint | summaries.rb:76:26:76:56 | call to source : | summaries.rb:76:8:76:57 | call to preserveTaint | $@ | summaries.rb:76:26:76:56 | call to source : | call to source : | +| summaries.rb:81:6:81:9 | ...[...] | summaries.rb:79:15:79:29 | call to source : | summaries.rb:81:6:81:9 | ...[...] | $@ | summaries.rb:79:15:79:29 | call to source : | call to source : | +| summaries.rb:81:6:81:9 | ...[...] | summaries.rb:79:15:79:29 | call to source : | summaries.rb:81:6:81:9 | ...[...] | $@ | summaries.rb:79:15:79:29 | call to source : | call to source : | +| summaries.rb:82:6:82:9 | ...[...] | summaries.rb:79:32:79:46 | call to source : | summaries.rb:82:6:82:9 | ...[...] | $@ | summaries.rb:79:32:79:46 | call to source : | call to source : | +| summaries.rb:82:6:82:9 | ...[...] | summaries.rb:79:32:79:46 | call to source : | summaries.rb:82:6:82:9 | ...[...] | $@ | summaries.rb:79:32:79:46 | call to source : | call to source : | +| summaries.rb:85:6:85:9 | ...[...] | summaries.rb:79:15:79:29 | call to source : | summaries.rb:85:6:85:9 | ...[...] | $@ | summaries.rb:79:15:79:29 | call to source : | call to source : | +| summaries.rb:85:6:85:9 | ...[...] | summaries.rb:79:15:79:29 | call to source : | summaries.rb:85:6:85:9 | ...[...] | $@ | summaries.rb:79:15:79:29 | call to source : | call to source : | +| summaries.rb:90:6:90:9 | ...[...] | summaries.rb:79:32:79:46 | call to source : | summaries.rb:90:6:90:9 | ...[...] | $@ | summaries.rb:79:32:79:46 | call to source : | call to source : | +| summaries.rb:90:6:90:9 | ...[...] | summaries.rb:79:32:79:46 | call to source : | summaries.rb:90:6:90:9 | ...[...] | $@ | summaries.rb:79:32:79:46 | call to source : | call to source : | +| summaries.rb:94:6:94:16 | call to get_value | summaries.rb:93:13:93:26 | call to source : | summaries.rb:94:6:94:16 | call to get_value | $@ | summaries.rb:93:13:93:26 | call to source : | call to source : | +| summaries.rb:94:6:94:16 | call to get_value | summaries.rb:93:13:93:26 | call to source : | summaries.rb:94:6:94:16 | call to get_value | $@ | summaries.rb:93:13:93:26 | call to source : | call to source : | +| summaries.rb:106:6:106:6 | y | summaries.rb:1:20:1:36 | call to source : | summaries.rb:106:6:106:6 | y | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | +| summaries.rb:107:6:107:6 | z | summaries.rb:1:20:1:36 | call to source : | summaries.rb:107:6:107:6 | z | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | +| summaries.rb:111:6:111:6 | x | summaries.rb:1:20:1:36 | call to source : | summaries.rb:111:6:111:6 | x | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | +| summaries.rb:113:16:113:22 | tainted | summaries.rb:1:20:1:36 | call to source : | summaries.rb:113:16:113:22 | tainted | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | +| summaries.rb:113:16:113:22 | tainted | summaries.rb:1:20:1:36 | call to source : | summaries.rb:113:16:113:22 | tainted | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | +| summaries.rb:114:21:114:27 | tainted | summaries.rb:1:20:1:36 | call to source : | summaries.rb:114:21:114:27 | tainted | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | +| summaries.rb:114:21:114:27 | tainted | summaries.rb:1:20:1:36 | call to source : | summaries.rb:114:21:114:27 | tainted | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | +| summaries.rb:117:26:117:32 | tainted | summaries.rb:1:20:1:36 | call to source : | summaries.rb:117:26:117:32 | tainted | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | +| summaries.rb:117:26:117:32 | tainted | summaries.rb:1:20:1:36 | call to source : | summaries.rb:117:26:117:32 | tainted | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | warning | CSV type row should have 5 columns but has 2: test;TooFewColumns | | CSV type row should have 5 columns but has 8: test;TooManyColumns;;;Member[Foo].Instance;too;many;columns | diff --git a/ruby/ql/test/library-tests/dataflow/summaries/summaries.rb b/ruby/ql/test/library-tests/dataflow/summaries/summaries.rb index baa662736f5..cf92b7948e7 100644 --- a/ruby/ql/test/library-tests/dataflow/summaries/summaries.rb +++ b/ruby/ql/test/library-tests/dataflow/summaries/summaries.rb @@ -50,6 +50,8 @@ end sink(Foo.namedArg(foo: tainted)) # $ hasTaintFlow=tainted sink(Foo.namedArg(tainted)) +args = { foo: source("tainted") } +sink(Foo.namedArg(**args)) # $ hasTaintFlow=tainted sink(Foo.anyArg(foo: tainted)) # $ hasTaintFlow=tainted sink(Foo.anyArg(tainted)) # $ hasTaintFlow=tainted From 02bea35da2f63c0049e556d4d75f8c1a39b29bed Mon Sep 17 00:00:00 2001 From: ihsinme Date: Mon, 8 Aug 2022 18:35:25 +0300 Subject: [PATCH 488/505] Update DangerousUseMbtowc.qhelp --- .../Security/CWE/CWE-125/DangerousUseMbtowc.qhelp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.qhelp index 5a9aa77214d..fd8842050dc 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.qhelp +++ b/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.qhelp @@ -3,7 +3,7 @@ "qhelp.dtd"> -

    Using function mbtowc with an invalid length argument can result in an out-of-bounds access error or unexpected result. If you are sure you are working with a null-terminated string, use the length macros, if not, use the correctly computed length.

    +

    Using a function to convert multibyte or wide characters with an invalid length argument may result in an out-of-range access error or unexpected results.

    @@ -20,4 +20,4 @@
  • - \ No newline at end of file + From 5ee499389ebc22babc3648144bdb30ff4dc19a33 Mon Sep 17 00:00:00 2001 From: ihsinme Date: Mon, 8 Aug 2022 18:36:53 +0300 Subject: [PATCH 489/505] Rename DangerousUseMbtowc.cpp to DangerousWorksWithMultibyteOrWideCharacters.cpp --- ...Mbtowc.cpp => DangerousWorksWithMultibyteOrWideCharacters.cpp} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename cpp/ql/src/experimental/Security/CWE/CWE-125/{DangerousUseMbtowc.cpp => DangerousWorksWithMultibyteOrWideCharacters.cpp} (100%) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.cpp b/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousWorksWithMultibyteOrWideCharacters.cpp similarity index 100% rename from cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.cpp rename to cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousWorksWithMultibyteOrWideCharacters.cpp From ef04b8f5b3262f4098ceca9b339ee8765747567c Mon Sep 17 00:00:00 2001 From: ihsinme Date: Mon, 8 Aug 2022 18:37:15 +0300 Subject: [PATCH 490/505] Rename DangerousUseMbtowc.qhelp to DangerousWorksWithMultibyteOrWideCharacters.qhelp --- ...wc.qhelp => DangerousWorksWithMultibyteOrWideCharacters.qhelp} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename cpp/ql/src/experimental/Security/CWE/CWE-125/{DangerousUseMbtowc.qhelp => DangerousWorksWithMultibyteOrWideCharacters.qhelp} (100%) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousWorksWithMultibyteOrWideCharacters.qhelp similarity index 100% rename from cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.qhelp rename to cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousWorksWithMultibyteOrWideCharacters.qhelp From bce395f201926d0a65a5d2546ef806bc89934041 Mon Sep 17 00:00:00 2001 From: ihsinme Date: Mon, 8 Aug 2022 18:38:24 +0300 Subject: [PATCH 491/505] Rename DangerousUseMbtowc.expected to DangerousWorksWithMultibyteOrWideCharacters.expected --- ...ected => DangerousWorksWithMultibyteOrWideCharacters.expected} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/{DangerousUseMbtowc.expected => DangerousWorksWithMultibyteOrWideCharacters.expected} (100%) diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousUseMbtowc.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousWorksWithMultibyteOrWideCharacters.expected similarity index 100% rename from cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousUseMbtowc.expected rename to cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousWorksWithMultibyteOrWideCharacters.expected From 9b5154f87864e99dd0d49c3dbcdcbbff9cd55332 Mon Sep 17 00:00:00 2001 From: ihsinme Date: Mon, 8 Aug 2022 18:39:10 +0300 Subject: [PATCH 492/505] Update and rename DangerousUseMbtowc.qlref to DangerousWorksWithMultibyteOrWideCharacters.qlref --- .../Security/CWE/CWE-125/semmle/tests/DangerousUseMbtowc.qlref | 1 - .../tests/DangerousWorksWithMultibyteOrWideCharacters.qlref | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousUseMbtowc.qlref create mode 100644 cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousWorksWithMultibyteOrWideCharacters.qlref diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousUseMbtowc.qlref b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousUseMbtowc.qlref deleted file mode 100644 index f8b0cddbb28..00000000000 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousUseMbtowc.qlref +++ /dev/null @@ -1 +0,0 @@ -experimental/Security/CWE/CWE-125/DangerousUseMbtowc.ql diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousWorksWithMultibyteOrWideCharacters.qlref b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousWorksWithMultibyteOrWideCharacters.qlref new file mode 100644 index 00000000000..228684a4e25 --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousWorksWithMultibyteOrWideCharacters.qlref @@ -0,0 +1 @@ +experimental/Security/CWE/CWE-125/DangerousWorksWithMultibyteOrWideCharacters.ql From 7cbf79b1443a94042f74c026f2886596ee04ab0a Mon Sep 17 00:00:00 2001 From: ihsinme Date: Mon, 8 Aug 2022 18:39:41 +0300 Subject: [PATCH 493/505] Rename DangerousUseMbtowc.ql to DangerousWorksWithMultibyteOrWideCharacters.ql --- ...seMbtowc.ql => DangerousWorksWithMultibyteOrWideCharacters.ql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename cpp/ql/src/experimental/Security/CWE/CWE-125/{DangerousUseMbtowc.ql => DangerousWorksWithMultibyteOrWideCharacters.ql} (100%) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.ql b/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousWorksWithMultibyteOrWideCharacters.ql similarity index 100% rename from cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousUseMbtowc.ql rename to cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousWorksWithMultibyteOrWideCharacters.ql From 212b1031b2ce6d739fb0d01bde9bdb37aa23b74e Mon Sep 17 00:00:00 2001 From: ihsinme Date: Mon, 8 Aug 2022 18:42:54 +0300 Subject: [PATCH 494/505] Update DangerousWorksWithMultibyteOrWideCharacters.qhelp --- .../CWE-125/DangerousWorksWithMultibyteOrWideCharacters.qhelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousWorksWithMultibyteOrWideCharacters.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousWorksWithMultibyteOrWideCharacters.qhelp index fd8842050dc..e6f18c5c8ae 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousWorksWithMultibyteOrWideCharacters.qhelp +++ b/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousWorksWithMultibyteOrWideCharacters.qhelp @@ -9,7 +9,7 @@

    The following example shows the erroneous and corrected method of using function mbtowc.

    - +
    From 4fdf4b23bd2e142cdc8fed959d5596f88dc1d66c Mon Sep 17 00:00:00 2001 From: ihsinme Date: Mon, 8 Aug 2022 18:46:39 +0300 Subject: [PATCH 495/505] Update DangerousWorksWithMultibyteOrWideCharacters.ql --- .../DangerousWorksWithMultibyteOrWideCharacters.ql | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousWorksWithMultibyteOrWideCharacters.ql b/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousWorksWithMultibyteOrWideCharacters.ql index 0b87d920b6f..f73ba21c39b 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousWorksWithMultibyteOrWideCharacters.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousWorksWithMultibyteOrWideCharacters.ql @@ -1,8 +1,8 @@ /** - * @name Dangerous use mbtowc. - * @description Using function mbtowc with an invalid length argument can result in an out-of-bounds access error or unexpected result. + * @name Dangerous use convert function. + * @description Using convert function with an invalid length argument can result in an out-of-bounds access error or unexpected result. * @kind problem - * @id cpp/dangerous-use-mbtowc + * @id cpp/dangerous-use-convert-function * @problem.severity warning * @precision medium * @tags correctness @@ -117,8 +117,7 @@ predicate findUseCharacterConversion(Expr exp, string msg) { predicate findUseMultibyteCharacter(Expr exp, string msg) { exists(ArrayType arrayType, ArrayExpr arrayExpr | arrayExpr = exp and - arrayExpr.getArrayBase().getType() = - arrayType and + arrayExpr.getArrayBase().getType() = arrayType and ( exists(AssignExpr assZero, SizeofExprOperator sizeofArray, Expr oneValue | oneValue.getValue() = "1" and From dc853d97283183f71ae38b931fb02aba340cef66 Mon Sep 17 00:00:00 2001 From: Harry Maclean Date: Fri, 29 Jul 2022 16:11:26 +1200 Subject: [PATCH 496/505] Ruby: Model ActiveRecord associations --- .../codeql/ruby/frameworks/ActiveRecord.qll | 172 ++++++++++++++++++ .../active_record/ActiveRecord.expected | 124 +++++++++++++ .../frameworks/active_record/associations.rb | 51 ++++++ 3 files changed, 347 insertions(+) create mode 100644 ruby/ql/test/library-tests/frameworks/active_record/associations.rb diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll index a983005e766..996fb2ec1a8 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll @@ -516,3 +516,175 @@ private module Persistence { override DataFlow::Node getValue() { assignNode.getRhs() = result.asExpr() } } } + +/** + * A method call inside an ActiveRecord model class that establishes an + * association between this model and another model. + * + * ```rb + * class User + * has_many :posts + * has_one :profile + * end + * ``` + */ +private class ActiveRecordAssociation extends DataFlow::CallNode { + private ActiveRecordModelClass modelClass; + + ActiveRecordAssociation() { + not exists(this.asExpr().getExpr().getEnclosingMethod()) and + this.asExpr().getExpr().getEnclosingModule() = modelClass and + this.getMethodName() = ["has_one", "has_many", "belongs_to", "has_and_belongs_to_many"] + } + + /** + * Gets the class which declares this association. + * For example, in + * ```rb + * class User + * has_many :posts + * end + * ``` + * the source class is `User`. + */ + ActiveRecordModelClass getSourceClass() { result = modelClass } + + /** + * Gets the class which this association refers to. + * For example, in + * ```rb + * class User + * has_many :posts + * end + * ``` + * the target class is `Post`. + */ + ActiveRecordModelClass getTargetClass() { + result.getName().toLowerCase() = this.getTargetModelName() + } + + /** + * Gets the (lowercase) name of the model this association targets. + * For example, in `has_many :posts`, this is `post`. + */ + string getTargetModelName() { + exists(string s | + s = this.getArgument(0).asExpr().getExpr().getConstantValue().getStringlikeValue() + | + // has_one :profile + // belongs_to :user + this.isSingular() and + result = s + or + // has_many :posts + // has_many :stories + this.isCollection() and + pluralize(result) = s + ) + } + + /** Holds if this association is one-to-one */ + predicate isSingular() { this.getMethodName() = ["has_one", "belongs_to"] } + + /** Holds if this association is one-to-many or many-to-many */ + predicate isCollection() { this.getMethodName() = ["has_many", "has_and_belongs_to_many"] } +} + +/** + * Converts `input` to plural form. + */ +bindingset[input] +bindingset[result] +private string pluralize(string input) { + exists(string stem | stem + "y" = input | result = stem + "ies") + or + result = input + "s" +} + +/** + * A call to a method generated by an ActiveRecord association. + * These yield ActiveRecord collection proxies, which act like collections but + * add some additional methods. + * We exclude `_changed?` and `_previously_changed?` because these + * do not yield ActiveRecord instances. + * https://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html + */ +private class ActiveRecordAssociationMethodCall extends DataFlow::CallNode { + ActiveRecordAssociation assoc; + + ActiveRecordAssociationMethodCall() { + exists(string model | model = assoc.getTargetModelName() | + this.getReceiver().(ActiveRecordInstance).getClass() = assoc.getSourceClass() and + ( + assoc.isCollection() and + ( + this.getMethodName() = pluralize(model) + ["", "=", "<<"] + or + this.getMethodName() = model + ["_ids", "_ids="] + ) + or + assoc.isSingular() and + ( + this.getMethodName() = model + ["", "="] or + this.getMethodName() = ["build_", "reload_"] + model or + this.getMethodName() = "create_" + model + ["!", ""] + ) + ) + ) + } + + ActiveRecordAssociation getAssociation() { result = assoc } +} + +/** + * A method call on an ActiveRecord collection proxy that yields one or more + * ActiveRecord instances. + * Example: + * ```rb + * class User < ActiveRecord::Base + * has_many :posts + * end + * + * User.new.posts.create + * ``` + * https://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html + */ +private class ActiveRecordCollectionProxyMethodCall extends DataFlow::CallNode { + ActiveRecordCollectionProxyMethodCall() { + this.getMethodName() = + [ + "push", "concat", "build", "create", "create!", "delete", "delete_all", "destroy", + "destroy_all", "find", "distinct", "reset", "reload" + ] and + ( + this.getReceiver().(ActiveRecordAssociationMethodCall).getAssociation().isCollection() + or + exists(ActiveRecordCollectionProxyMethodCall receiver | receiver = this.getReceiver() | + receiver.getAssociation().isCollection() and + receiver.getMethodName() = ["reset", "reload", "distinct"] + ) + ) + } + + ActiveRecordAssociation getAssociation() { + result = this.getReceiver().(ActiveRecordAssociationMethodCall).getAssociation() + } +} + +/** + * A call to an association method which yields ActiveRecord instances. + */ +private class ActiveRecordAssociationModelInstantiation extends ActiveRecordModelInstantiation instanceof ActiveRecordAssociationMethodCall { + override ActiveRecordModelClass getClass() { + result = this.(ActiveRecordAssociationMethodCall).getAssociation().getTargetClass() + } +} + +/** + * A call to a method on a collection proxy which yields ActiveRecord instances. + */ +private class ActiveRecordCollectionProxyModelInstantiation extends ActiveRecordModelInstantiation instanceof ActiveRecordCollectionProxyMethodCall { + override ActiveRecordModelClass getClass() { + result = this.(ActiveRecordCollectionProxyMethodCall).getAssociation().getTargetClass() + } +} diff --git a/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.expected b/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.expected index d4022297559..b483e30921a 100644 --- a/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.expected +++ b/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.expected @@ -2,15 +2,94 @@ activeRecordModelClasses | ActiveRecord.rb:1:1:3:3 | UserGroup | | ActiveRecord.rb:5:1:15:3 | User | | ActiveRecord.rb:17:1:21:3 | Admin | +| associations.rb:1:1:3:3 | Author | +| associations.rb:5:1:9:3 | Post | +| associations.rb:11:1:13:3 | Tag | +| associations.rb:15:1:17:3 | Comment | activeRecordInstances | ActiveRecord.rb:9:5:9:68 | call to find | | ActiveRecord.rb:13:5:13:40 | call to find_by | +| ActiveRecord.rb:13:5:13:46 | call to users | | ActiveRecord.rb:36:5:36:30 | call to find_by_name | | ActiveRecord.rb:55:5:57:7 | if ... | | ActiveRecord.rb:55:43:56:40 | then ... | | ActiveRecord.rb:56:7:56:40 | call to find_by | | ActiveRecord.rb:60:5:60:33 | call to find_by | | ActiveRecord.rb:62:5:62:34 | call to find | +| associations.rb:19:1:19:20 | ... = ... | +| associations.rb:19:1:19:20 | ... = ... | +| associations.rb:19:11:19:20 | call to new | +| associations.rb:21:1:21:28 | ... = ... | +| associations.rb:21:1:21:28 | ... = ... | +| associations.rb:21:9:21:15 | author1 | +| associations.rb:21:9:21:21 | call to posts | +| associations.rb:21:9:21:28 | call to create | +| associations.rb:23:1:23:32 | ... = ... | +| associations.rb:23:12:23:16 | post1 | +| associations.rb:23:12:23:25 | call to comments | +| associations.rb:23:12:23:32 | call to create | +| associations.rb:25:1:25:22 | ... = ... | +| associations.rb:25:1:25:22 | ... = ... | +| associations.rb:25:11:25:15 | post1 | +| associations.rb:25:11:25:22 | call to author | +| associations.rb:27:1:27:28 | ... = ... | +| associations.rb:27:1:27:28 | ... = ... | +| associations.rb:27:9:27:15 | author2 | +| associations.rb:27:9:27:21 | call to posts | +| associations.rb:27:9:27:28 | call to create | +| associations.rb:29:1:29:7 | author2 | +| associations.rb:29:1:29:13 | call to posts | +| associations.rb:29:18:29:22 | post2 | +| associations.rb:33:1:33:5 | post2 | +| associations.rb:33:1:33:14 | call to comments | +| associations.rb:33:1:33:21 | call to create | +| associations.rb:35:1:35:7 | author1 | +| associations.rb:35:1:35:13 | call to posts | +| associations.rb:35:1:35:20 | call to reload | +| associations.rb:35:1:35:27 | call to create | +| associations.rb:37:1:37:5 | post1 | +| associations.rb:38:1:38:5 | post1 | +| associations.rb:40:1:40:7 | author1 | +| associations.rb:40:1:40:13 | call to posts | +| associations.rb:40:1:40:25 | call to push | +| associations.rb:40:20:40:24 | post2 | +| associations.rb:41:1:41:7 | author1 | +| associations.rb:41:1:41:13 | call to posts | +| associations.rb:41:1:41:27 | call to concat | +| associations.rb:41:22:41:26 | post2 | +| associations.rb:42:1:42:7 | author1 | +| associations.rb:42:1:42:13 | call to posts | +| associations.rb:42:1:42:19 | call to build | +| associations.rb:43:1:43:7 | author1 | +| associations.rb:43:1:43:13 | call to posts | +| associations.rb:43:1:43:20 | call to create | +| associations.rb:44:1:44:7 | author1 | +| associations.rb:44:1:44:13 | call to posts | +| associations.rb:44:1:44:21 | call to create! | +| associations.rb:45:1:45:7 | author1 | +| associations.rb:45:1:45:13 | call to posts | +| associations.rb:45:1:45:20 | call to delete | +| associations.rb:46:1:46:7 | author1 | +| associations.rb:46:1:46:13 | call to posts | +| associations.rb:46:1:46:24 | call to delete_all | +| associations.rb:47:1:47:7 | author1 | +| associations.rb:47:1:47:13 | call to posts | +| associations.rb:47:1:47:21 | call to destroy | +| associations.rb:48:1:48:7 | author1 | +| associations.rb:48:1:48:13 | call to posts | +| associations.rb:48:1:48:25 | call to destroy_all | +| associations.rb:49:1:49:7 | author1 | +| associations.rb:49:1:49:13 | call to posts | +| associations.rb:49:1:49:22 | call to distinct | +| associations.rb:49:1:49:36 | call to find | +| associations.rb:50:1:50:7 | author1 | +| associations.rb:50:1:50:13 | call to posts | +| associations.rb:50:1:50:19 | call to reset | +| associations.rb:50:1:50:33 | call to find | +| associations.rb:51:1:51:7 | author1 | +| associations.rb:51:1:51:13 | call to posts | +| associations.rb:51:1:51:20 | call to reload | +| associations.rb:51:1:51:34 | call to find | activeRecordSqlExecutionRanges | ActiveRecord.rb:9:33:9:67 | "name='#{...}' and pass='#{...}'" | | ActiveRecord.rb:19:16:19:24 | condition | @@ -53,6 +132,13 @@ activeRecordModelClassMethodCalls | ActiveRecord.rb:92:5:92:71 | call to update | | ActiveRecord.rb:98:13:98:54 | call to annotate | | ActiveRecord.rb:102:13:102:77 | call to annotate | +| associations.rb:2:3:2:17 | call to has_many | +| associations.rb:6:3:6:20 | call to belongs_to | +| associations.rb:7:3:7:20 | call to has_many | +| associations.rb:8:3:8:31 | call to has_and_belongs_to_many | +| associations.rb:12:3:12:32 | call to has_and_belongs_to_many | +| associations.rb:16:3:16:18 | call to belongs_to | +| associations.rb:19:11:19:20 | call to new | potentiallyUnsafeSqlExecutingMethodCall | ActiveRecord.rb:9:5:9:68 | call to find | | ActiveRecord.rb:19:5:19:25 | call to destroy_by | @@ -68,10 +154,48 @@ potentiallyUnsafeSqlExecutingMethodCall activeRecordModelInstantiations | ActiveRecord.rb:9:5:9:68 | call to find | ActiveRecord.rb:5:1:15:3 | User | | ActiveRecord.rb:13:5:13:40 | call to find_by | ActiveRecord.rb:1:1:3:3 | UserGroup | +| ActiveRecord.rb:13:5:13:46 | call to users | ActiveRecord.rb:5:1:15:3 | User | | ActiveRecord.rb:36:5:36:30 | call to find_by_name | ActiveRecord.rb:5:1:15:3 | User | | ActiveRecord.rb:56:7:56:40 | call to find_by | ActiveRecord.rb:5:1:15:3 | User | | ActiveRecord.rb:60:5:60:33 | call to find_by | ActiveRecord.rb:5:1:15:3 | User | | ActiveRecord.rb:62:5:62:34 | call to find | ActiveRecord.rb:5:1:15:3 | User | +| associations.rb:19:11:19:20 | call to new | associations.rb:1:1:3:3 | Author | +| associations.rb:21:9:21:21 | call to posts | associations.rb:5:1:9:3 | Post | +| associations.rb:21:9:21:28 | call to create | associations.rb:5:1:9:3 | Post | +| associations.rb:23:12:23:25 | call to comments | associations.rb:15:1:17:3 | Comment | +| associations.rb:23:12:23:32 | call to create | associations.rb:15:1:17:3 | Comment | +| associations.rb:25:11:25:22 | call to author | associations.rb:1:1:3:3 | Author | +| associations.rb:27:9:27:21 | call to posts | associations.rb:5:1:9:3 | Post | +| associations.rb:27:9:27:28 | call to create | associations.rb:5:1:9:3 | Post | +| associations.rb:29:1:29:13 | call to posts | associations.rb:5:1:9:3 | Post | +| associations.rb:33:1:33:14 | call to comments | associations.rb:15:1:17:3 | Comment | +| associations.rb:33:1:33:21 | call to create | associations.rb:15:1:17:3 | Comment | +| associations.rb:35:1:35:13 | call to posts | associations.rb:5:1:9:3 | Post | +| associations.rb:35:1:35:20 | call to reload | associations.rb:5:1:9:3 | Post | +| associations.rb:40:1:40:13 | call to posts | associations.rb:5:1:9:3 | Post | +| associations.rb:40:1:40:25 | call to push | associations.rb:5:1:9:3 | Post | +| associations.rb:41:1:41:13 | call to posts | associations.rb:5:1:9:3 | Post | +| associations.rb:41:1:41:27 | call to concat | associations.rb:5:1:9:3 | Post | +| associations.rb:42:1:42:13 | call to posts | associations.rb:5:1:9:3 | Post | +| associations.rb:42:1:42:19 | call to build | associations.rb:5:1:9:3 | Post | +| associations.rb:43:1:43:13 | call to posts | associations.rb:5:1:9:3 | Post | +| associations.rb:43:1:43:20 | call to create | associations.rb:5:1:9:3 | Post | +| associations.rb:44:1:44:13 | call to posts | associations.rb:5:1:9:3 | Post | +| associations.rb:44:1:44:21 | call to create! | associations.rb:5:1:9:3 | Post | +| associations.rb:45:1:45:13 | call to posts | associations.rb:5:1:9:3 | Post | +| associations.rb:45:1:45:20 | call to delete | associations.rb:5:1:9:3 | Post | +| associations.rb:46:1:46:13 | call to posts | associations.rb:5:1:9:3 | Post | +| associations.rb:46:1:46:24 | call to delete_all | associations.rb:5:1:9:3 | Post | +| associations.rb:47:1:47:13 | call to posts | associations.rb:5:1:9:3 | Post | +| associations.rb:47:1:47:21 | call to destroy | associations.rb:5:1:9:3 | Post | +| associations.rb:48:1:48:13 | call to posts | associations.rb:5:1:9:3 | Post | +| associations.rb:48:1:48:25 | call to destroy_all | associations.rb:5:1:9:3 | Post | +| associations.rb:49:1:49:13 | call to posts | associations.rb:5:1:9:3 | Post | +| associations.rb:49:1:49:22 | call to distinct | associations.rb:5:1:9:3 | Post | +| associations.rb:50:1:50:13 | call to posts | associations.rb:5:1:9:3 | Post | +| associations.rb:50:1:50:19 | call to reset | associations.rb:5:1:9:3 | Post | +| associations.rb:51:1:51:13 | call to posts | associations.rb:5:1:9:3 | Post | +| associations.rb:51:1:51:20 | call to reload | associations.rb:5:1:9:3 | Post | persistentWriteAccesses | ActiveRecord.rb:72:5:72:24 | call to create | ActiveRecord.rb:72:18:72:23 | call to params | | ActiveRecord.rb:76:5:76:66 | call to create | ActiveRecord.rb:76:24:76:36 | ...[...] | diff --git a/ruby/ql/test/library-tests/frameworks/active_record/associations.rb b/ruby/ql/test/library-tests/frameworks/active_record/associations.rb new file mode 100644 index 00000000000..1fa671d1e1d --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/active_record/associations.rb @@ -0,0 +1,51 @@ +class Author < ActiveRecord::Base + has_many :posts +end + +class Post < ActiveRecord::Base + belongs_to :author + has_many :comments + has_and_belongs_to_many :tags +end + +class Tag < ActiveRecord::Base + has_and_belongs_to_many :posts +end + +class Comment < ActiveRecord::Base + belongs_to :post +end + +author1 = Author.new + +post1 = author1.posts.create + +comment1 = post1.comments.create + +author2 = post1.author + +post2 = author2.posts.create + +author2.posts << post2 + +# The final method call in this chain should not be recognised as an +# instantiation. +post2.comments.create.create + +author1.posts.reload.create + +post1.build_tag +post1.build_tag + +author1.posts.push(post2) +author1.posts.concat(post2) +author1.posts.build +author1.posts.create +author1.posts.create! +author1.posts.delete +author1.posts.delete_all +author1.posts.destroy +author1.posts.destroy_all +author1.posts.distinct.find(post_id) +author1.posts.reset.find(post_id) +author1.posts.reload.find(post_id) From 58b628b6d1f133849281602a447e03488462be9e Mon Sep 17 00:00:00 2001 From: Harry Maclean Date: Fri, 5 Aug 2022 11:59:33 +1200 Subject: [PATCH 497/505] Ruby: Add change note --- .../change-notes/2022-08-05-active-record-associations.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ruby/ql/lib/change-notes/2022-08-05-active-record-associations.md diff --git a/ruby/ql/lib/change-notes/2022-08-05-active-record-associations.md b/ruby/ql/lib/change-notes/2022-08-05-active-record-associations.md new file mode 100644 index 00000000000..9fa4d0a6cd5 --- /dev/null +++ b/ruby/ql/lib/change-notes/2022-08-05-active-record-associations.md @@ -0,0 +1,6 @@ +--- +category: minorAnalysis +--- +* Calls to methods generated by ActiveRecord associations are now recognised as + instantiations of ActiveRecord objects. This increases the sensitivity of + queries such as `rb/sql-injection` and `rb/stored-xss`. From 831f7224024deaf88bd72e6a346d30fa7b9f3967 Mon Sep 17 00:00:00 2001 From: Harry Maclean Date: Tue, 9 Aug 2022 13:37:53 +1200 Subject: [PATCH 498/505] Ruby: Make room for new test --- .../active_record/ActiveRecord.expected | 96 +++++++++---------- .../frameworks/active_record/associations.rb | 2 + 2 files changed, 50 insertions(+), 48 deletions(-) diff --git a/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.expected b/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.expected index b483e30921a..e54eefefc1b 100644 --- a/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.expected +++ b/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.expected @@ -40,56 +40,56 @@ activeRecordInstances | associations.rb:29:1:29:7 | author2 | | associations.rb:29:1:29:13 | call to posts | | associations.rb:29:18:29:22 | post2 | -| associations.rb:33:1:33:5 | post2 | -| associations.rb:33:1:33:14 | call to comments | -| associations.rb:33:1:33:21 | call to create | -| associations.rb:35:1:35:7 | author1 | -| associations.rb:35:1:35:13 | call to posts | -| associations.rb:35:1:35:20 | call to reload | -| associations.rb:35:1:35:27 | call to create | -| associations.rb:37:1:37:5 | post1 | -| associations.rb:38:1:38:5 | post1 | -| associations.rb:40:1:40:7 | author1 | -| associations.rb:40:1:40:13 | call to posts | -| associations.rb:40:1:40:25 | call to push | -| associations.rb:40:20:40:24 | post2 | -| associations.rb:41:1:41:7 | author1 | -| associations.rb:41:1:41:13 | call to posts | -| associations.rb:41:1:41:27 | call to concat | -| associations.rb:41:22:41:26 | post2 | +| associations.rb:35:1:35:5 | post2 | +| associations.rb:35:1:35:14 | call to comments | +| associations.rb:35:1:35:21 | call to create | +| associations.rb:37:1:37:7 | author1 | +| associations.rb:37:1:37:13 | call to posts | +| associations.rb:37:1:37:20 | call to reload | +| associations.rb:37:1:37:27 | call to create | +| associations.rb:39:1:39:5 | post1 | +| associations.rb:40:1:40:5 | post1 | | associations.rb:42:1:42:7 | author1 | | associations.rb:42:1:42:13 | call to posts | -| associations.rb:42:1:42:19 | call to build | +| associations.rb:42:1:42:25 | call to push | +| associations.rb:42:20:42:24 | post2 | | associations.rb:43:1:43:7 | author1 | | associations.rb:43:1:43:13 | call to posts | -| associations.rb:43:1:43:20 | call to create | +| associations.rb:43:1:43:27 | call to concat | +| associations.rb:43:22:43:26 | post2 | | associations.rb:44:1:44:7 | author1 | | associations.rb:44:1:44:13 | call to posts | -| associations.rb:44:1:44:21 | call to create! | +| associations.rb:44:1:44:19 | call to build | | associations.rb:45:1:45:7 | author1 | | associations.rb:45:1:45:13 | call to posts | -| associations.rb:45:1:45:20 | call to delete | +| associations.rb:45:1:45:20 | call to create | | associations.rb:46:1:46:7 | author1 | | associations.rb:46:1:46:13 | call to posts | -| associations.rb:46:1:46:24 | call to delete_all | +| associations.rb:46:1:46:21 | call to create! | | associations.rb:47:1:47:7 | author1 | | associations.rb:47:1:47:13 | call to posts | -| associations.rb:47:1:47:21 | call to destroy | +| associations.rb:47:1:47:20 | call to delete | | associations.rb:48:1:48:7 | author1 | | associations.rb:48:1:48:13 | call to posts | -| associations.rb:48:1:48:25 | call to destroy_all | +| associations.rb:48:1:48:24 | call to delete_all | | associations.rb:49:1:49:7 | author1 | | associations.rb:49:1:49:13 | call to posts | -| associations.rb:49:1:49:22 | call to distinct | -| associations.rb:49:1:49:36 | call to find | +| associations.rb:49:1:49:21 | call to destroy | | associations.rb:50:1:50:7 | author1 | | associations.rb:50:1:50:13 | call to posts | -| associations.rb:50:1:50:19 | call to reset | -| associations.rb:50:1:50:33 | call to find | +| associations.rb:50:1:50:25 | call to destroy_all | | associations.rb:51:1:51:7 | author1 | | associations.rb:51:1:51:13 | call to posts | -| associations.rb:51:1:51:20 | call to reload | -| associations.rb:51:1:51:34 | call to find | +| associations.rb:51:1:51:22 | call to distinct | +| associations.rb:51:1:51:36 | call to find | +| associations.rb:52:1:52:7 | author1 | +| associations.rb:52:1:52:13 | call to posts | +| associations.rb:52:1:52:19 | call to reset | +| associations.rb:52:1:52:33 | call to find | +| associations.rb:53:1:53:7 | author1 | +| associations.rb:53:1:53:13 | call to posts | +| associations.rb:53:1:53:20 | call to reload | +| associations.rb:53:1:53:34 | call to find | activeRecordSqlExecutionRanges | ActiveRecord.rb:9:33:9:67 | "name='#{...}' and pass='#{...}'" | | ActiveRecord.rb:19:16:19:24 | condition | @@ -168,34 +168,34 @@ activeRecordModelInstantiations | associations.rb:27:9:27:21 | call to posts | associations.rb:5:1:9:3 | Post | | associations.rb:27:9:27:28 | call to create | associations.rb:5:1:9:3 | Post | | associations.rb:29:1:29:13 | call to posts | associations.rb:5:1:9:3 | Post | -| associations.rb:33:1:33:14 | call to comments | associations.rb:15:1:17:3 | Comment | -| associations.rb:33:1:33:21 | call to create | associations.rb:15:1:17:3 | Comment | -| associations.rb:35:1:35:13 | call to posts | associations.rb:5:1:9:3 | Post | -| associations.rb:35:1:35:20 | call to reload | associations.rb:5:1:9:3 | Post | -| associations.rb:40:1:40:13 | call to posts | associations.rb:5:1:9:3 | Post | -| associations.rb:40:1:40:25 | call to push | associations.rb:5:1:9:3 | Post | -| associations.rb:41:1:41:13 | call to posts | associations.rb:5:1:9:3 | Post | -| associations.rb:41:1:41:27 | call to concat | associations.rb:5:1:9:3 | Post | +| associations.rb:35:1:35:14 | call to comments | associations.rb:15:1:17:3 | Comment | +| associations.rb:35:1:35:21 | call to create | associations.rb:15:1:17:3 | Comment | +| associations.rb:37:1:37:13 | call to posts | associations.rb:5:1:9:3 | Post | +| associations.rb:37:1:37:20 | call to reload | associations.rb:5:1:9:3 | Post | | associations.rb:42:1:42:13 | call to posts | associations.rb:5:1:9:3 | Post | -| associations.rb:42:1:42:19 | call to build | associations.rb:5:1:9:3 | Post | +| associations.rb:42:1:42:25 | call to push | associations.rb:5:1:9:3 | Post | | associations.rb:43:1:43:13 | call to posts | associations.rb:5:1:9:3 | Post | -| associations.rb:43:1:43:20 | call to create | associations.rb:5:1:9:3 | Post | +| associations.rb:43:1:43:27 | call to concat | associations.rb:5:1:9:3 | Post | | associations.rb:44:1:44:13 | call to posts | associations.rb:5:1:9:3 | Post | -| associations.rb:44:1:44:21 | call to create! | associations.rb:5:1:9:3 | Post | +| associations.rb:44:1:44:19 | call to build | associations.rb:5:1:9:3 | Post | | associations.rb:45:1:45:13 | call to posts | associations.rb:5:1:9:3 | Post | -| associations.rb:45:1:45:20 | call to delete | associations.rb:5:1:9:3 | Post | +| associations.rb:45:1:45:20 | call to create | associations.rb:5:1:9:3 | Post | | associations.rb:46:1:46:13 | call to posts | associations.rb:5:1:9:3 | Post | -| associations.rb:46:1:46:24 | call to delete_all | associations.rb:5:1:9:3 | Post | +| associations.rb:46:1:46:21 | call to create! | associations.rb:5:1:9:3 | Post | | associations.rb:47:1:47:13 | call to posts | associations.rb:5:1:9:3 | Post | -| associations.rb:47:1:47:21 | call to destroy | associations.rb:5:1:9:3 | Post | +| associations.rb:47:1:47:20 | call to delete | associations.rb:5:1:9:3 | Post | | associations.rb:48:1:48:13 | call to posts | associations.rb:5:1:9:3 | Post | -| associations.rb:48:1:48:25 | call to destroy_all | associations.rb:5:1:9:3 | Post | +| associations.rb:48:1:48:24 | call to delete_all | associations.rb:5:1:9:3 | Post | | associations.rb:49:1:49:13 | call to posts | associations.rb:5:1:9:3 | Post | -| associations.rb:49:1:49:22 | call to distinct | associations.rb:5:1:9:3 | Post | +| associations.rb:49:1:49:21 | call to destroy | associations.rb:5:1:9:3 | Post | | associations.rb:50:1:50:13 | call to posts | associations.rb:5:1:9:3 | Post | -| associations.rb:50:1:50:19 | call to reset | associations.rb:5:1:9:3 | Post | +| associations.rb:50:1:50:25 | call to destroy_all | associations.rb:5:1:9:3 | Post | | associations.rb:51:1:51:13 | call to posts | associations.rb:5:1:9:3 | Post | -| associations.rb:51:1:51:20 | call to reload | associations.rb:5:1:9:3 | Post | +| associations.rb:51:1:51:22 | call to distinct | associations.rb:5:1:9:3 | Post | +| associations.rb:52:1:52:13 | call to posts | associations.rb:5:1:9:3 | Post | +| associations.rb:52:1:52:19 | call to reset | associations.rb:5:1:9:3 | Post | +| associations.rb:53:1:53:13 | call to posts | associations.rb:5:1:9:3 | Post | +| associations.rb:53:1:53:20 | call to reload | associations.rb:5:1:9:3 | Post | persistentWriteAccesses | ActiveRecord.rb:72:5:72:24 | call to create | ActiveRecord.rb:72:18:72:23 | call to params | | ActiveRecord.rb:76:5:76:66 | call to create | ActiveRecord.rb:76:24:76:36 | ...[...] | diff --git a/ruby/ql/test/library-tests/frameworks/active_record/associations.rb b/ruby/ql/test/library-tests/frameworks/active_record/associations.rb index 1fa671d1e1d..786a96d07aa 100644 --- a/ruby/ql/test/library-tests/frameworks/active_record/associations.rb +++ b/ruby/ql/test/library-tests/frameworks/active_record/associations.rb @@ -28,6 +28,8 @@ post2 = author2.posts.create author2.posts << post2 + + # The final method call in this chain should not be recognised as an # instantiation. post2.comments.create.create From e3115b5ed74dc55c5eb15b767661c867c683153d Mon Sep 17 00:00:00 2001 From: Harry Maclean Date: Tue, 9 Aug 2022 13:40:30 +1200 Subject: [PATCH 499/505] Ruby: Add test for other= --- .../frameworks/active_record/ActiveRecord.expected | 9 +++++++++ .../frameworks/active_record/associations.rb | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.expected b/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.expected index e54eefefc1b..e01a7800b8c 100644 --- a/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.expected +++ b/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.expected @@ -40,6 +40,13 @@ activeRecordInstances | associations.rb:29:1:29:7 | author2 | | associations.rb:29:1:29:13 | call to posts | | associations.rb:29:18:29:22 | post2 | +| associations.rb:31:1:31:5 | post1 | +| associations.rb:31:1:31:12 | __synth__0 | +| associations.rb:31:1:31:12 | call to author= | +| associations.rb:31:1:31:22 | ... | +| associations.rb:31:16:31:22 | ... = ... | +| associations.rb:31:16:31:22 | ... = ... | +| associations.rb:31:16:31:22 | author2 | | associations.rb:35:1:35:5 | post2 | | associations.rb:35:1:35:14 | call to comments | | associations.rb:35:1:35:21 | call to create | @@ -168,6 +175,7 @@ activeRecordModelInstantiations | associations.rb:27:9:27:21 | call to posts | associations.rb:5:1:9:3 | Post | | associations.rb:27:9:27:28 | call to create | associations.rb:5:1:9:3 | Post | | associations.rb:29:1:29:13 | call to posts | associations.rb:5:1:9:3 | Post | +| associations.rb:31:1:31:12 | call to author= | associations.rb:1:1:3:3 | Author | | associations.rb:35:1:35:14 | call to comments | associations.rb:15:1:17:3 | Comment | | associations.rb:35:1:35:21 | call to create | associations.rb:15:1:17:3 | Comment | | associations.rb:37:1:37:13 | call to posts | associations.rb:5:1:9:3 | Post | @@ -206,3 +214,4 @@ persistentWriteAccesses | ActiveRecord.rb:88:5:88:69 | call to update | ActiveRecord.rb:88:27:88:39 | ...[...] | | ActiveRecord.rb:88:5:88:69 | call to update | ActiveRecord.rb:88:52:88:68 | ...[...] | | ActiveRecord.rb:92:5:92:71 | call to update | ActiveRecord.rb:92:21:92:70 | call to [] | +| associations.rb:31:16:31:22 | ... = ... | associations.rb:31:16:31:22 | author2 | diff --git a/ruby/ql/test/library-tests/frameworks/active_record/associations.rb b/ruby/ql/test/library-tests/frameworks/active_record/associations.rb index 786a96d07aa..891ee45fb72 100644 --- a/ruby/ql/test/library-tests/frameworks/active_record/associations.rb +++ b/ruby/ql/test/library-tests/frameworks/active_record/associations.rb @@ -28,7 +28,7 @@ post2 = author2.posts.create author2.posts << post2 - +post1.author = author2 # The final method call in this chain should not be recognised as an # instantiation. From 22d7b046ab034d67c0440d831c2f617f68b74a74 Mon Sep 17 00:00:00 2001 From: Harry Maclean Date: Tue, 9 Aug 2022 13:42:29 +1200 Subject: [PATCH 500/505] Ruby: Fix << --- ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll | 4 +++- .../frameworks/active_record/ActiveRecord.expected | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll index 996fb2ec1a8..a4994118b5c 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll @@ -618,7 +618,9 @@ private class ActiveRecordAssociationMethodCall extends DataFlow::CallNode { ( assoc.isCollection() and ( - this.getMethodName() = pluralize(model) + ["", "=", "<<"] + this.getMethodName() = pluralize(model) + ["", "="] + or + this.getMethodName() = "<<" or this.getMethodName() = model + ["_ids", "_ids="] ) diff --git a/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.expected b/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.expected index e01a7800b8c..a77e70879ac 100644 --- a/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.expected +++ b/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.expected @@ -39,6 +39,7 @@ activeRecordInstances | associations.rb:27:9:27:28 | call to create | | associations.rb:29:1:29:7 | author2 | | associations.rb:29:1:29:13 | call to posts | +| associations.rb:29:1:29:22 | ... << ... | | associations.rb:29:18:29:22 | post2 | | associations.rb:31:1:31:5 | post1 | | associations.rb:31:1:31:12 | __synth__0 | @@ -175,6 +176,8 @@ activeRecordModelInstantiations | associations.rb:27:9:27:21 | call to posts | associations.rb:5:1:9:3 | Post | | associations.rb:27:9:27:28 | call to create | associations.rb:5:1:9:3 | Post | | associations.rb:29:1:29:13 | call to posts | associations.rb:5:1:9:3 | Post | +| associations.rb:29:1:29:22 | ... << ... | associations.rb:11:1:13:3 | Tag | +| associations.rb:29:1:29:22 | ... << ... | associations.rb:15:1:17:3 | Comment | | associations.rb:31:1:31:12 | call to author= | associations.rb:1:1:3:3 | Author | | associations.rb:35:1:35:14 | call to comments | associations.rb:15:1:17:3 | Comment | | associations.rb:35:1:35:21 | call to create | associations.rb:15:1:17:3 | Comment | From 2cab1ed076e9ce2df4203ed86a8b506134ad2c0a Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Tue, 9 Aug 2022 07:59:21 +0200 Subject: [PATCH 501/505] Fix path of `fetch-codeql` --- .github/workflows/csv-coverage-timeseries.yml | 2 +- .github/workflows/csv-coverage-update.yml | 2 +- .github/workflows/csv-coverage.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/csv-coverage-timeseries.yml b/.github/workflows/csv-coverage-timeseries.yml index ea216f68949..42fd4711dac 100644 --- a/.github/workflows/csv-coverage-timeseries.yml +++ b/.github/workflows/csv-coverage-timeseries.yml @@ -22,7 +22,7 @@ jobs: with: python-version: 3.8 - name: Download CodeQL CLI - uses: ./.github/actions/fetch-codeql + uses: ./script/.github/actions/fetch-codeql - name: Build modeled package list run: | python script/misc/scripts/library-coverage/generate-timeseries.py codeqlModels diff --git a/.github/workflows/csv-coverage-update.yml b/.github/workflows/csv-coverage-update.yml index 1de2149ce2e..6474044c483 100644 --- a/.github/workflows/csv-coverage-update.yml +++ b/.github/workflows/csv-coverage-update.yml @@ -26,7 +26,7 @@ jobs: with: python-version: 3.8 - name: Download CodeQL CLI - uses: ./.github/actions/fetch-codeql + uses: ./ql/.github/actions/fetch-codeql - name: Generate coverage files run: | python ql/misc/scripts/library-coverage/generate-report.py ci ql ql diff --git a/.github/workflows/csv-coverage.yml b/.github/workflows/csv-coverage.yml index e829957a0d3..e330490b69b 100644 --- a/.github/workflows/csv-coverage.yml +++ b/.github/workflows/csv-coverage.yml @@ -26,7 +26,7 @@ jobs: with: python-version: 3.8 - name: Download CodeQL CLI - uses: ./.github/actions/fetch-codeql + uses: ./script/.github/actions/fetch-codeql - name: Build modeled package list run: | python script/misc/scripts/library-coverage/generate-report.py ci codeqlModels script From 28c8d9b88522f1a2dd6ace35b34f282b08da0471 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 9 Aug 2022 14:17:07 +0200 Subject: [PATCH 502/505] Ruby: Add two more hash flow tests --- .../dataflow/hash-flow/hash-flow.expected | 2282 +++++++++-------- .../dataflow/hash-flow/hash_flow.rb | 8 + 2 files changed, 1159 insertions(+), 1131 deletions(-) diff --git a/ruby/ql/test/library-tests/dataflow/hash-flow/hash-flow.expected b/ruby/ql/test/library-tests/dataflow/hash-flow/hash-flow.expected index 2d2aa24b2df..76d7b77a729 100644 --- a/ruby/ql/test/library-tests/dataflow/hash-flow/hash-flow.expected +++ b/ruby/ql/test/library-tests/dataflow/hash-flow/hash-flow.expected @@ -55,473 +55,481 @@ edges | hash_flow.rb:68:13:68:39 | ...[...] [element :a] : | hash_flow.rb:69:10:69:14 | hash4 [element :a] : | | hash_flow.rb:68:22:68:31 | call to taint : | hash_flow.rb:68:13:68:39 | ...[...] [element :a] : | | hash_flow.rb:69:10:69:14 | hash4 [element :a] : | hash_flow.rb:69:10:69:18 | ...[...] | -| hash_flow.rb:76:13:76:42 | call to [] [element :a] : | hash_flow.rb:77:10:77:14 | hash1 [element :a] : | -| hash_flow.rb:76:26:76:35 | call to taint : | hash_flow.rb:76:13:76:42 | call to [] [element :a] : | -| hash_flow.rb:77:10:77:14 | hash1 [element :a] : | hash_flow.rb:77:10:77:18 | ...[...] | -| hash_flow.rb:85:15:85:24 | call to taint : | hash_flow.rb:88:30:88:33 | hash [element :a] : | -| hash_flow.rb:88:13:88:34 | call to try_convert [element :a] : | hash_flow.rb:89:10:89:14 | hash2 [element :a] : | -| hash_flow.rb:88:30:88:33 | hash [element :a] : | hash_flow.rb:88:13:88:34 | call to try_convert [element :a] : | -| hash_flow.rb:89:10:89:14 | hash2 [element :a] : | hash_flow.rb:89:10:89:18 | ...[...] | -| hash_flow.rb:97:21:97:30 | call to taint : | hash_flow.rb:98:10:98:10 | b | -| hash_flow.rb:105:9:105:12 | [post] hash [element :a] : | hash_flow.rb:106:10:106:13 | hash [element :a] : | -| hash_flow.rb:105:9:105:34 | call to store : | hash_flow.rb:107:10:107:10 | b | -| hash_flow.rb:105:24:105:33 | call to taint : | hash_flow.rb:105:9:105:12 | [post] hash [element :a] : | -| hash_flow.rb:105:24:105:33 | call to taint : | hash_flow.rb:105:9:105:34 | call to store : | -| hash_flow.rb:106:10:106:13 | hash [element :a] : | hash_flow.rb:106:10:106:17 | ...[...] | -| hash_flow.rb:110:9:110:12 | [post] hash [element] : | hash_flow.rb:111:10:111:13 | hash [element] : | -| hash_flow.rb:110:9:110:12 | [post] hash [element] : | hash_flow.rb:112:10:112:13 | hash [element] : | -| hash_flow.rb:110:9:110:33 | call to store : | hash_flow.rb:113:10:113:10 | c | -| hash_flow.rb:110:23:110:32 | call to taint : | hash_flow.rb:110:9:110:12 | [post] hash [element] : | -| hash_flow.rb:110:23:110:32 | call to taint : | hash_flow.rb:110:9:110:33 | call to store : | -| hash_flow.rb:111:10:111:13 | hash [element] : | hash_flow.rb:111:10:111:17 | ...[...] | -| hash_flow.rb:112:10:112:13 | hash [element] : | hash_flow.rb:112:10:112:17 | ...[...] | -| hash_flow.rb:120:15:120:24 | call to taint : | hash_flow.rb:123:5:123:8 | hash [element :a] : | -| hash_flow.rb:120:15:120:24 | call to taint : | hash_flow.rb:126:5:126:8 | hash [element :a] : | -| hash_flow.rb:123:5:123:8 | hash [element :a] : | hash_flow.rb:123:18:123:29 | key_or_value : | -| hash_flow.rb:123:18:123:29 | key_or_value : | hash_flow.rb:124:14:124:25 | key_or_value | -| hash_flow.rb:126:5:126:8 | hash [element :a] : | hash_flow.rb:126:22:126:26 | value : | -| hash_flow.rb:126:22:126:26 | value : | hash_flow.rb:128:14:128:18 | value | -| hash_flow.rb:136:15:136:25 | call to taint : | hash_flow.rb:139:9:139:12 | hash [element :a] : | -| hash_flow.rb:136:15:136:25 | call to taint : | hash_flow.rb:143:9:143:12 | hash [element :a] : | -| hash_flow.rb:139:9:139:12 | hash [element :a] : | hash_flow.rb:139:9:139:22 | call to assoc [element 1] : | -| hash_flow.rb:139:9:139:22 | call to assoc [element 1] : | hash_flow.rb:141:10:141:10 | b [element 1] : | -| hash_flow.rb:139:9:139:22 | call to assoc [element 1] : | hash_flow.rb:142:10:142:10 | b [element 1] : | -| hash_flow.rb:141:10:141:10 | b [element 1] : | hash_flow.rb:141:10:141:13 | ...[...] | -| hash_flow.rb:142:10:142:10 | b [element 1] : | hash_flow.rb:142:10:142:13 | ...[...] | -| hash_flow.rb:143:9:143:12 | hash [element :a] : | hash_flow.rb:143:9:143:21 | call to assoc [element 1] : | -| hash_flow.rb:143:9:143:21 | call to assoc [element 1] : | hash_flow.rb:144:10:144:10 | c [element 1] : | -| hash_flow.rb:144:10:144:10 | c [element 1] : | hash_flow.rb:144:10:144:13 | ...[...] | -| hash_flow.rb:162:15:162:25 | call to taint : | hash_flow.rb:165:9:165:12 | hash [element :a] : | -| hash_flow.rb:165:9:165:12 | hash [element :a] : | hash_flow.rb:165:9:165:20 | call to compact [element :a] : | -| hash_flow.rb:165:9:165:20 | call to compact [element :a] : | hash_flow.rb:166:10:166:10 | a [element :a] : | -| hash_flow.rb:166:10:166:10 | a [element :a] : | hash_flow.rb:166:10:166:14 | ...[...] | -| hash_flow.rb:174:15:174:25 | call to taint : | hash_flow.rb:177:9:177:12 | hash [element :a] : | -| hash_flow.rb:177:9:177:12 | hash [element :a] : | hash_flow.rb:177:9:177:23 | call to delete : | -| hash_flow.rb:177:9:177:23 | call to delete : | hash_flow.rb:178:10:178:10 | a | -| hash_flow.rb:186:15:186:25 | call to taint : | hash_flow.rb:189:9:189:12 | hash [element :a] : | -| hash_flow.rb:186:15:186:25 | call to taint : | hash_flow.rb:194:10:194:13 | hash [element :a] : | -| hash_flow.rb:189:9:189:12 | [post] hash [element :a] : | hash_flow.rb:194:10:194:13 | hash [element :a] : | -| hash_flow.rb:189:9:189:12 | hash [element :a] : | hash_flow.rb:189:9:189:12 | [post] hash [element :a] : | -| hash_flow.rb:189:9:189:12 | hash [element :a] : | hash_flow.rb:189:9:192:7 | call to delete_if [element :a] : | -| hash_flow.rb:189:9:189:12 | hash [element :a] : | hash_flow.rb:189:33:189:37 | value : | -| hash_flow.rb:189:9:192:7 | call to delete_if [element :a] : | hash_flow.rb:193:10:193:10 | a [element :a] : | -| hash_flow.rb:189:33:189:37 | value : | hash_flow.rb:191:14:191:18 | value | -| hash_flow.rb:193:10:193:10 | a [element :a] : | hash_flow.rb:193:10:193:14 | ...[...] | -| hash_flow.rb:194:10:194:13 | hash [element :a] : | hash_flow.rb:194:10:194:17 | ...[...] | -| hash_flow.rb:202:15:202:25 | call to taint : | hash_flow.rb:209:10:209:13 | hash [element :a] : | -| hash_flow.rb:205:19:205:29 | call to taint : | hash_flow.rb:211:10:211:13 | hash [element :c, element :d] : | -| hash_flow.rb:209:10:209:13 | hash [element :a] : | hash_flow.rb:209:10:209:21 | call to dig | -| hash_flow.rb:211:10:211:13 | hash [element :c, element :d] : | hash_flow.rb:211:10:211:24 | call to dig | -| hash_flow.rb:219:15:219:25 | call to taint : | hash_flow.rb:222:9:222:12 | hash [element :a] : | -| hash_flow.rb:222:9:222:12 | hash [element :a] : | hash_flow.rb:222:9:225:7 | call to each [element :a] : | -| hash_flow.rb:222:9:222:12 | hash [element :a] : | hash_flow.rb:222:28:222:32 | value : | -| hash_flow.rb:222:9:225:7 | call to each [element :a] : | hash_flow.rb:226:10:226:10 | x [element :a] : | -| hash_flow.rb:222:28:222:32 | value : | hash_flow.rb:224:14:224:18 | value | -| hash_flow.rb:226:10:226:10 | x [element :a] : | hash_flow.rb:226:10:226:14 | ...[...] | -| hash_flow.rb:234:15:234:25 | call to taint : | hash_flow.rb:237:9:237:12 | hash [element :a] : | -| hash_flow.rb:237:9:237:12 | hash [element :a] : | hash_flow.rb:237:9:239:7 | call to each_key [element :a] : | -| hash_flow.rb:237:9:239:7 | call to each_key [element :a] : | hash_flow.rb:240:10:240:10 | x [element :a] : | -| hash_flow.rb:240:10:240:10 | x [element :a] : | hash_flow.rb:240:10:240:14 | ...[...] | -| hash_flow.rb:248:15:248:25 | call to taint : | hash_flow.rb:251:9:251:12 | hash [element :a] : | -| hash_flow.rb:251:9:251:12 | hash [element :a] : | hash_flow.rb:251:9:254:7 | call to each_pair [element :a] : | -| hash_flow.rb:251:9:251:12 | hash [element :a] : | hash_flow.rb:251:33:251:37 | value : | -| hash_flow.rb:251:9:254:7 | call to each_pair [element :a] : | hash_flow.rb:255:10:255:10 | x [element :a] : | -| hash_flow.rb:251:33:251:37 | value : | hash_flow.rb:253:14:253:18 | value | -| hash_flow.rb:255:10:255:10 | x [element :a] : | hash_flow.rb:255:10:255:14 | ...[...] | -| hash_flow.rb:263:15:263:25 | call to taint : | hash_flow.rb:266:9:266:12 | hash [element :a] : | -| hash_flow.rb:266:9:266:12 | hash [element :a] : | hash_flow.rb:266:9:268:7 | call to each_value [element :a] : | -| hash_flow.rb:266:9:266:12 | hash [element :a] : | hash_flow.rb:266:29:266:33 | value : | -| hash_flow.rb:266:9:268:7 | call to each_value [element :a] : | hash_flow.rb:269:10:269:10 | x [element :a] : | -| hash_flow.rb:266:29:266:33 | value : | hash_flow.rb:267:14:267:18 | value | -| hash_flow.rb:269:10:269:10 | x [element :a] : | hash_flow.rb:269:10:269:14 | ...[...] | -| hash_flow.rb:279:15:279:25 | call to taint : | hash_flow.rb:282:9:282:12 | hash [element :c] : | -| hash_flow.rb:282:9:282:12 | hash [element :c] : | hash_flow.rb:282:9:282:28 | call to except [element :c] : | -| hash_flow.rb:282:9:282:28 | call to except [element :c] : | hash_flow.rb:285:10:285:10 | x [element :c] : | -| hash_flow.rb:285:10:285:10 | x [element :c] : | hash_flow.rb:285:10:285:14 | ...[...] | -| hash_flow.rb:293:15:293:25 | call to taint : | hash_flow.rb:297:9:297:12 | hash [element :a] : | -| hash_flow.rb:293:15:293:25 | call to taint : | hash_flow.rb:301:9:301:12 | hash [element :a] : | -| hash_flow.rb:293:15:293:25 | call to taint : | hash_flow.rb:303:9:303:12 | hash [element :a] : | -| hash_flow.rb:293:15:293:25 | call to taint : | hash_flow.rb:307:9:307:12 | hash [element :a] : | -| hash_flow.rb:295:15:295:25 | call to taint : | hash_flow.rb:297:9:297:12 | hash [element :c] : | -| hash_flow.rb:295:15:295:25 | call to taint : | hash_flow.rb:307:9:307:12 | hash [element :c] : | -| hash_flow.rb:297:9:297:12 | hash [element :a] : | hash_flow.rb:297:9:299:7 | call to fetch : | -| hash_flow.rb:297:9:297:12 | hash [element :c] : | hash_flow.rb:297:9:299:7 | call to fetch : | -| hash_flow.rb:297:9:299:7 | call to fetch : | hash_flow.rb:300:10:300:10 | b | -| hash_flow.rb:297:20:297:30 | call to taint : | hash_flow.rb:297:37:297:37 | x : | -| hash_flow.rb:297:37:297:37 | x : | hash_flow.rb:298:14:298:14 | x | -| hash_flow.rb:301:9:301:12 | hash [element :a] : | hash_flow.rb:301:9:301:22 | call to fetch : | -| hash_flow.rb:301:9:301:22 | call to fetch : | hash_flow.rb:302:10:302:10 | b | -| hash_flow.rb:303:9:303:12 | hash [element :a] : | hash_flow.rb:303:9:303:35 | call to fetch : | -| hash_flow.rb:303:9:303:35 | call to fetch : | hash_flow.rb:304:10:304:10 | b | -| hash_flow.rb:303:24:303:34 | call to taint : | hash_flow.rb:303:9:303:35 | call to fetch : | -| hash_flow.rb:305:9:305:35 | call to fetch : | hash_flow.rb:306:10:306:10 | b | -| hash_flow.rb:305:24:305:34 | call to taint : | hash_flow.rb:305:9:305:35 | call to fetch : | -| hash_flow.rb:307:9:307:12 | hash [element :a] : | hash_flow.rb:307:9:307:34 | call to fetch : | -| hash_flow.rb:307:9:307:12 | hash [element :c] : | hash_flow.rb:307:9:307:34 | call to fetch : | -| hash_flow.rb:307:9:307:34 | call to fetch : | hash_flow.rb:308:10:308:10 | b | -| hash_flow.rb:307:23:307:33 | call to taint : | hash_flow.rb:307:9:307:34 | call to fetch : | -| hash_flow.rb:315:15:315:25 | call to taint : | hash_flow.rb:319:9:319:12 | hash [element :a] : | -| hash_flow.rb:315:15:315:25 | call to taint : | hash_flow.rb:324:9:324:12 | hash [element :a] : | -| hash_flow.rb:315:15:315:25 | call to taint : | hash_flow.rb:326:9:326:12 | hash [element :a] : | -| hash_flow.rb:317:15:317:25 | call to taint : | hash_flow.rb:319:9:319:12 | hash [element :c] : | -| hash_flow.rb:317:15:317:25 | call to taint : | hash_flow.rb:326:9:326:12 | hash [element :c] : | -| hash_flow.rb:319:9:319:12 | hash [element :a] : | hash_flow.rb:319:9:322:7 | call to fetch_values [element] : | -| hash_flow.rb:319:9:319:12 | hash [element :c] : | hash_flow.rb:319:9:322:7 | call to fetch_values [element] : | -| hash_flow.rb:319:9:322:7 | call to fetch_values [element] : | hash_flow.rb:323:10:323:10 | b [element] : | -| hash_flow.rb:319:27:319:37 | call to taint : | hash_flow.rb:319:44:319:44 | x : | -| hash_flow.rb:319:44:319:44 | x : | hash_flow.rb:320:14:320:14 | x | -| hash_flow.rb:321:9:321:19 | call to taint : | hash_flow.rb:319:9:322:7 | call to fetch_values [element] : | -| hash_flow.rb:323:10:323:10 | b [element] : | hash_flow.rb:323:10:323:13 | ...[...] | -| hash_flow.rb:324:9:324:12 | hash [element :a] : | hash_flow.rb:324:9:324:29 | call to fetch_values [element] : | -| hash_flow.rb:324:9:324:29 | call to fetch_values [element] : | hash_flow.rb:325:10:325:10 | b [element] : | -| hash_flow.rb:325:10:325:10 | b [element] : | hash_flow.rb:325:10:325:13 | ...[...] | -| hash_flow.rb:326:9:326:12 | hash [element :a] : | hash_flow.rb:326:9:326:31 | call to fetch_values [element] : | -| hash_flow.rb:326:9:326:12 | hash [element :c] : | hash_flow.rb:326:9:326:31 | call to fetch_values [element] : | -| hash_flow.rb:326:9:326:31 | call to fetch_values [element] : | hash_flow.rb:327:10:327:10 | b [element] : | -| hash_flow.rb:327:10:327:10 | b [element] : | hash_flow.rb:327:10:327:13 | ...[...] | -| hash_flow.rb:334:15:334:25 | call to taint : | hash_flow.rb:338:9:338:12 | hash [element :a] : | -| hash_flow.rb:336:15:336:25 | call to taint : | hash_flow.rb:338:9:338:12 | hash [element :c] : | -| hash_flow.rb:338:9:338:12 | hash [element :a] : | hash_flow.rb:338:9:342:7 | call to filter [element :a] : | -| hash_flow.rb:338:9:338:12 | hash [element :a] : | hash_flow.rb:338:30:338:34 | value : | -| hash_flow.rb:338:9:338:12 | hash [element :c] : | hash_flow.rb:338:30:338:34 | value : | -| hash_flow.rb:338:9:342:7 | call to filter [element :a] : | hash_flow.rb:343:11:343:11 | b [element :a] : | -| hash_flow.rb:338:30:338:34 | value : | hash_flow.rb:340:14:340:18 | value | -| hash_flow.rb:343:11:343:11 | b [element :a] : | hash_flow.rb:343:11:343:15 | ...[...] : | -| hash_flow.rb:343:11:343:15 | ...[...] : | hash_flow.rb:343:10:343:16 | ( ... ) | -| hash_flow.rb:350:15:350:25 | call to taint : | hash_flow.rb:354:5:354:8 | hash [element :a] : | -| hash_flow.rb:352:15:352:25 | call to taint : | hash_flow.rb:354:5:354:8 | hash [element :c] : | -| hash_flow.rb:354:5:354:8 | [post] hash [element :a] : | hash_flow.rb:359:11:359:14 | hash [element :a] : | -| hash_flow.rb:354:5:354:8 | hash [element :a] : | hash_flow.rb:354:5:354:8 | [post] hash [element :a] : | -| hash_flow.rb:354:5:354:8 | hash [element :a] : | hash_flow.rb:354:27:354:31 | value : | -| hash_flow.rb:354:5:354:8 | hash [element :c] : | hash_flow.rb:354:27:354:31 | value : | -| hash_flow.rb:354:27:354:31 | value : | hash_flow.rb:356:14:356:18 | value | -| hash_flow.rb:359:11:359:14 | hash [element :a] : | hash_flow.rb:359:11:359:18 | ...[...] : | -| hash_flow.rb:359:11:359:18 | ...[...] : | hash_flow.rb:359:10:359:19 | ( ... ) | -| hash_flow.rb:366:15:366:25 | call to taint : | hash_flow.rb:370:9:370:12 | hash [element :a] : | -| hash_flow.rb:368:15:368:25 | call to taint : | hash_flow.rb:370:9:370:12 | hash [element :c] : | -| hash_flow.rb:370:9:370:12 | hash [element :a] : | hash_flow.rb:370:9:370:20 | call to flatten [element] : | -| hash_flow.rb:370:9:370:12 | hash [element :c] : | hash_flow.rb:370:9:370:20 | call to flatten [element] : | -| hash_flow.rb:370:9:370:20 | call to flatten [element] : | hash_flow.rb:371:11:371:11 | b [element] : | -| hash_flow.rb:371:11:371:11 | b [element] : | hash_flow.rb:371:11:371:14 | ...[...] : | -| hash_flow.rb:371:11:371:14 | ...[...] : | hash_flow.rb:371:10:371:15 | ( ... ) | -| hash_flow.rb:378:15:378:25 | call to taint : | hash_flow.rb:382:9:382:12 | hash [element :a] : | -| hash_flow.rb:380:15:380:25 | call to taint : | hash_flow.rb:382:9:382:12 | hash [element :c] : | -| hash_flow.rb:382:9:382:12 | [post] hash [element :a] : | hash_flow.rb:387:11:387:14 | hash [element :a] : | -| hash_flow.rb:382:9:382:12 | hash [element :a] : | hash_flow.rb:382:9:382:12 | [post] hash [element :a] : | -| hash_flow.rb:382:9:382:12 | hash [element :a] : | hash_flow.rb:382:9:386:7 | call to keep_if [element :a] : | -| hash_flow.rb:382:9:382:12 | hash [element :a] : | hash_flow.rb:382:31:382:35 | value : | -| hash_flow.rb:382:9:382:12 | hash [element :c] : | hash_flow.rb:382:31:382:35 | value : | -| hash_flow.rb:382:9:386:7 | call to keep_if [element :a] : | hash_flow.rb:388:11:388:11 | b [element :a] : | -| hash_flow.rb:382:31:382:35 | value : | hash_flow.rb:384:14:384:18 | value | -| hash_flow.rb:387:11:387:14 | hash [element :a] : | hash_flow.rb:387:11:387:18 | ...[...] : | -| hash_flow.rb:387:11:387:18 | ...[...] : | hash_flow.rb:387:10:387:19 | ( ... ) | -| hash_flow.rb:388:11:388:11 | b [element :a] : | hash_flow.rb:388:11:388:15 | ...[...] : | -| hash_flow.rb:388:11:388:15 | ...[...] : | hash_flow.rb:388:10:388:16 | ( ... ) | -| hash_flow.rb:395:15:395:25 | call to taint : | hash_flow.rb:404:12:404:16 | hash1 [element :a] : | -| hash_flow.rb:397:15:397:25 | call to taint : | hash_flow.rb:404:12:404:16 | hash1 [element :c] : | -| hash_flow.rb:400:15:400:25 | call to taint : | hash_flow.rb:404:24:404:28 | hash2 [element :d] : | -| hash_flow.rb:402:15:402:25 | call to taint : | hash_flow.rb:404:24:404:28 | hash2 [element :f] : | -| hash_flow.rb:404:12:404:16 | hash1 [element :a] : | hash_flow.rb:404:12:408:7 | call to merge [element :a] : | -| hash_flow.rb:404:12:404:16 | hash1 [element :a] : | hash_flow.rb:404:40:404:48 | old_value : | -| hash_flow.rb:404:12:404:16 | hash1 [element :a] : | hash_flow.rb:404:51:404:59 | new_value : | -| hash_flow.rb:404:12:404:16 | hash1 [element :c] : | hash_flow.rb:404:12:408:7 | call to merge [element :c] : | -| hash_flow.rb:404:12:404:16 | hash1 [element :c] : | hash_flow.rb:404:40:404:48 | old_value : | -| hash_flow.rb:404:12:404:16 | hash1 [element :c] : | hash_flow.rb:404:51:404:59 | new_value : | -| hash_flow.rb:404:12:408:7 | call to merge [element :a] : | hash_flow.rb:409:11:409:14 | hash [element :a] : | -| hash_flow.rb:404:12:408:7 | call to merge [element :c] : | hash_flow.rb:411:11:411:14 | hash [element :c] : | -| hash_flow.rb:404:12:408:7 | call to merge [element :d] : | hash_flow.rb:412:11:412:14 | hash [element :d] : | -| hash_flow.rb:404:12:408:7 | call to merge [element :f] : | hash_flow.rb:414:11:414:14 | hash [element :f] : | -| hash_flow.rb:404:24:404:28 | hash2 [element :d] : | hash_flow.rb:404:12:408:7 | call to merge [element :d] : | -| hash_flow.rb:404:24:404:28 | hash2 [element :d] : | hash_flow.rb:404:40:404:48 | old_value : | -| hash_flow.rb:404:24:404:28 | hash2 [element :d] : | hash_flow.rb:404:51:404:59 | new_value : | -| hash_flow.rb:404:24:404:28 | hash2 [element :f] : | hash_flow.rb:404:12:408:7 | call to merge [element :f] : | -| hash_flow.rb:404:24:404:28 | hash2 [element :f] : | hash_flow.rb:404:40:404:48 | old_value : | -| hash_flow.rb:404:24:404:28 | hash2 [element :f] : | hash_flow.rb:404:51:404:59 | new_value : | -| hash_flow.rb:404:40:404:48 | old_value : | hash_flow.rb:406:14:406:22 | old_value | -| hash_flow.rb:404:51:404:59 | new_value : | hash_flow.rb:407:14:407:22 | new_value | -| hash_flow.rb:409:11:409:14 | hash [element :a] : | hash_flow.rb:409:11:409:18 | ...[...] : | -| hash_flow.rb:409:11:409:18 | ...[...] : | hash_flow.rb:409:10:409:19 | ( ... ) | -| hash_flow.rb:411:11:411:14 | hash [element :c] : | hash_flow.rb:411:11:411:18 | ...[...] : | -| hash_flow.rb:411:11:411:18 | ...[...] : | hash_flow.rb:411:10:411:19 | ( ... ) | -| hash_flow.rb:412:11:412:14 | hash [element :d] : | hash_flow.rb:412:11:412:18 | ...[...] : | -| hash_flow.rb:412:11:412:18 | ...[...] : | hash_flow.rb:412:10:412:19 | ( ... ) | -| hash_flow.rb:414:11:414:14 | hash [element :f] : | hash_flow.rb:414:11:414:18 | ...[...] : | -| hash_flow.rb:414:11:414:18 | ...[...] : | hash_flow.rb:414:10:414:19 | ( ... ) | -| hash_flow.rb:421:15:421:25 | call to taint : | hash_flow.rb:430:12:430:16 | hash1 [element :a] : | -| hash_flow.rb:423:15:423:25 | call to taint : | hash_flow.rb:430:12:430:16 | hash1 [element :c] : | -| hash_flow.rb:426:15:426:25 | call to taint : | hash_flow.rb:430:25:430:29 | hash2 [element :d] : | -| hash_flow.rb:428:15:428:25 | call to taint : | hash_flow.rb:430:25:430:29 | hash2 [element :f] : | -| hash_flow.rb:430:12:430:16 | [post] hash1 [element :a] : | hash_flow.rb:442:11:442:15 | hash1 [element :a] : | -| hash_flow.rb:430:12:430:16 | [post] hash1 [element :c] : | hash_flow.rb:444:11:444:15 | hash1 [element :c] : | -| hash_flow.rb:430:12:430:16 | [post] hash1 [element :d] : | hash_flow.rb:445:11:445:15 | hash1 [element :d] : | -| hash_flow.rb:430:12:430:16 | [post] hash1 [element :f] : | hash_flow.rb:447:11:447:15 | hash1 [element :f] : | -| hash_flow.rb:430:12:430:16 | hash1 [element :a] : | hash_flow.rb:430:12:430:16 | [post] hash1 [element :a] : | -| hash_flow.rb:430:12:430:16 | hash1 [element :a] : | hash_flow.rb:430:12:434:7 | call to merge! [element :a] : | -| hash_flow.rb:430:12:430:16 | hash1 [element :a] : | hash_flow.rb:430:41:430:49 | old_value : | -| hash_flow.rb:430:12:430:16 | hash1 [element :a] : | hash_flow.rb:430:52:430:60 | new_value : | -| hash_flow.rb:430:12:430:16 | hash1 [element :c] : | hash_flow.rb:430:12:430:16 | [post] hash1 [element :c] : | -| hash_flow.rb:430:12:430:16 | hash1 [element :c] : | hash_flow.rb:430:12:434:7 | call to merge! [element :c] : | -| hash_flow.rb:430:12:430:16 | hash1 [element :c] : | hash_flow.rb:430:41:430:49 | old_value : | -| hash_flow.rb:430:12:430:16 | hash1 [element :c] : | hash_flow.rb:430:52:430:60 | new_value : | -| hash_flow.rb:430:12:434:7 | call to merge! [element :a] : | hash_flow.rb:435:11:435:14 | hash [element :a] : | -| hash_flow.rb:430:12:434:7 | call to merge! [element :c] : | hash_flow.rb:437:11:437:14 | hash [element :c] : | -| hash_flow.rb:430:12:434:7 | call to merge! [element :d] : | hash_flow.rb:438:11:438:14 | hash [element :d] : | -| hash_flow.rb:430:12:434:7 | call to merge! [element :f] : | hash_flow.rb:440:11:440:14 | hash [element :f] : | -| hash_flow.rb:430:25:430:29 | hash2 [element :d] : | hash_flow.rb:430:12:430:16 | [post] hash1 [element :d] : | -| hash_flow.rb:430:25:430:29 | hash2 [element :d] : | hash_flow.rb:430:12:434:7 | call to merge! [element :d] : | -| hash_flow.rb:430:25:430:29 | hash2 [element :d] : | hash_flow.rb:430:41:430:49 | old_value : | -| hash_flow.rb:430:25:430:29 | hash2 [element :d] : | hash_flow.rb:430:52:430:60 | new_value : | -| hash_flow.rb:430:25:430:29 | hash2 [element :f] : | hash_flow.rb:430:12:430:16 | [post] hash1 [element :f] : | -| hash_flow.rb:430:25:430:29 | hash2 [element :f] : | hash_flow.rb:430:12:434:7 | call to merge! [element :f] : | -| hash_flow.rb:430:25:430:29 | hash2 [element :f] : | hash_flow.rb:430:41:430:49 | old_value : | -| hash_flow.rb:430:25:430:29 | hash2 [element :f] : | hash_flow.rb:430:52:430:60 | new_value : | -| hash_flow.rb:430:41:430:49 | old_value : | hash_flow.rb:432:14:432:22 | old_value | -| hash_flow.rb:430:52:430:60 | new_value : | hash_flow.rb:433:14:433:22 | new_value | -| hash_flow.rb:435:11:435:14 | hash [element :a] : | hash_flow.rb:435:11:435:18 | ...[...] : | -| hash_flow.rb:435:11:435:18 | ...[...] : | hash_flow.rb:435:10:435:19 | ( ... ) | -| hash_flow.rb:437:11:437:14 | hash [element :c] : | hash_flow.rb:437:11:437:18 | ...[...] : | -| hash_flow.rb:437:11:437:18 | ...[...] : | hash_flow.rb:437:10:437:19 | ( ... ) | -| hash_flow.rb:438:11:438:14 | hash [element :d] : | hash_flow.rb:438:11:438:18 | ...[...] : | -| hash_flow.rb:438:11:438:18 | ...[...] : | hash_flow.rb:438:10:438:19 | ( ... ) | -| hash_flow.rb:440:11:440:14 | hash [element :f] : | hash_flow.rb:440:11:440:18 | ...[...] : | -| hash_flow.rb:440:11:440:18 | ...[...] : | hash_flow.rb:440:10:440:19 | ( ... ) | -| hash_flow.rb:442:11:442:15 | hash1 [element :a] : | hash_flow.rb:442:11:442:19 | ...[...] : | -| hash_flow.rb:442:11:442:19 | ...[...] : | hash_flow.rb:442:10:442:20 | ( ... ) | -| hash_flow.rb:444:11:444:15 | hash1 [element :c] : | hash_flow.rb:444:11:444:19 | ...[...] : | -| hash_flow.rb:444:11:444:19 | ...[...] : | hash_flow.rb:444:10:444:20 | ( ... ) | -| hash_flow.rb:445:11:445:15 | hash1 [element :d] : | hash_flow.rb:445:11:445:19 | ...[...] : | -| hash_flow.rb:445:11:445:19 | ...[...] : | hash_flow.rb:445:10:445:20 | ( ... ) | -| hash_flow.rb:447:11:447:15 | hash1 [element :f] : | hash_flow.rb:447:11:447:19 | ...[...] : | -| hash_flow.rb:447:11:447:19 | ...[...] : | hash_flow.rb:447:10:447:20 | ( ... ) | -| hash_flow.rb:454:15:454:25 | call to taint : | hash_flow.rb:457:9:457:12 | hash [element :a] : | -| hash_flow.rb:457:9:457:12 | hash [element :a] : | hash_flow.rb:457:9:457:22 | call to rassoc [element 1] : | -| hash_flow.rb:457:9:457:22 | call to rassoc [element 1] : | hash_flow.rb:459:10:459:10 | b [element 1] : | -| hash_flow.rb:459:10:459:10 | b [element 1] : | hash_flow.rb:459:10:459:13 | ...[...] | -| hash_flow.rb:466:15:466:25 | call to taint : | hash_flow.rb:469:9:469:12 | hash [element :a] : | -| hash_flow.rb:469:9:469:12 | hash [element :a] : | hash_flow.rb:469:9:473:7 | call to reject [element :a] : | -| hash_flow.rb:469:9:469:12 | hash [element :a] : | hash_flow.rb:469:29:469:33 | value : | -| hash_flow.rb:469:9:473:7 | call to reject [element :a] : | hash_flow.rb:474:10:474:10 | b [element :a] : | -| hash_flow.rb:469:29:469:33 | value : | hash_flow.rb:471:14:471:18 | value | -| hash_flow.rb:474:10:474:10 | b [element :a] : | hash_flow.rb:474:10:474:14 | ...[...] | -| hash_flow.rb:481:15:481:25 | call to taint : | hash_flow.rb:484:9:484:12 | hash [element :a] : | -| hash_flow.rb:481:15:481:25 | call to taint : | hash_flow.rb:490:10:490:13 | hash [element :a] : | -| hash_flow.rb:484:9:484:12 | [post] hash [element :a] : | hash_flow.rb:490:10:490:13 | hash [element :a] : | -| hash_flow.rb:484:9:484:12 | hash [element :a] : | hash_flow.rb:484:9:484:12 | [post] hash [element :a] : | -| hash_flow.rb:484:9:484:12 | hash [element :a] : | hash_flow.rb:484:9:488:7 | call to reject! [element :a] : | -| hash_flow.rb:484:9:484:12 | hash [element :a] : | hash_flow.rb:484:30:484:34 | value : | -| hash_flow.rb:484:9:488:7 | call to reject! [element :a] : | hash_flow.rb:489:10:489:10 | b [element :a] : | -| hash_flow.rb:484:30:484:34 | value : | hash_flow.rb:486:14:486:18 | value | -| hash_flow.rb:489:10:489:10 | b [element :a] : | hash_flow.rb:489:10:489:14 | ...[...] | -| hash_flow.rb:490:10:490:13 | hash [element :a] : | hash_flow.rb:490:10:490:17 | ...[...] | -| hash_flow.rb:497:15:497:25 | call to taint : | hash_flow.rb:504:19:504:22 | hash [element :a] : | -| hash_flow.rb:499:15:499:25 | call to taint : | hash_flow.rb:504:19:504:22 | hash [element :c] : | -| hash_flow.rb:504:5:504:9 | [post] hash2 [element :a] : | hash_flow.rb:505:11:505:15 | hash2 [element :a] : | -| hash_flow.rb:504:5:504:9 | [post] hash2 [element :c] : | hash_flow.rb:507:11:507:15 | hash2 [element :c] : | -| hash_flow.rb:504:19:504:22 | hash [element :a] : | hash_flow.rb:504:5:504:9 | [post] hash2 [element :a] : | -| hash_flow.rb:504:19:504:22 | hash [element :c] : | hash_flow.rb:504:5:504:9 | [post] hash2 [element :c] : | -| hash_flow.rb:505:11:505:15 | hash2 [element :a] : | hash_flow.rb:505:11:505:19 | ...[...] : | -| hash_flow.rb:505:11:505:19 | ...[...] : | hash_flow.rb:505:10:505:20 | ( ... ) | -| hash_flow.rb:507:11:507:15 | hash2 [element :c] : | hash_flow.rb:507:11:507:19 | ...[...] : | -| hash_flow.rb:507:11:507:19 | ...[...] : | hash_flow.rb:507:10:507:20 | ( ... ) | -| hash_flow.rb:512:15:512:25 | call to taint : | hash_flow.rb:516:9:516:12 | hash [element :a] : | -| hash_flow.rb:514:15:514:25 | call to taint : | hash_flow.rb:516:9:516:12 | hash [element :c] : | -| hash_flow.rb:516:9:516:12 | hash [element :a] : | hash_flow.rb:516:9:520:7 | call to select [element :a] : | -| hash_flow.rb:516:9:516:12 | hash [element :a] : | hash_flow.rb:516:30:516:34 | value : | -| hash_flow.rb:516:9:516:12 | hash [element :c] : | hash_flow.rb:516:30:516:34 | value : | -| hash_flow.rb:516:9:520:7 | call to select [element :a] : | hash_flow.rb:521:11:521:11 | b [element :a] : | -| hash_flow.rb:516:30:516:34 | value : | hash_flow.rb:518:14:518:18 | value | -| hash_flow.rb:521:11:521:11 | b [element :a] : | hash_flow.rb:521:11:521:15 | ...[...] : | -| hash_flow.rb:521:11:521:15 | ...[...] : | hash_flow.rb:521:10:521:16 | ( ... ) | -| hash_flow.rb:528:15:528:25 | call to taint : | hash_flow.rb:532:5:532:8 | hash [element :a] : | -| hash_flow.rb:530:15:530:25 | call to taint : | hash_flow.rb:532:5:532:8 | hash [element :c] : | -| hash_flow.rb:532:5:532:8 | [post] hash [element :a] : | hash_flow.rb:537:11:537:14 | hash [element :a] : | -| hash_flow.rb:532:5:532:8 | hash [element :a] : | hash_flow.rb:532:5:532:8 | [post] hash [element :a] : | -| hash_flow.rb:532:5:532:8 | hash [element :a] : | hash_flow.rb:532:27:532:31 | value : | -| hash_flow.rb:532:5:532:8 | hash [element :c] : | hash_flow.rb:532:27:532:31 | value : | -| hash_flow.rb:532:27:532:31 | value : | hash_flow.rb:534:14:534:18 | value | -| hash_flow.rb:537:11:537:14 | hash [element :a] : | hash_flow.rb:537:11:537:18 | ...[...] : | -| hash_flow.rb:537:11:537:18 | ...[...] : | hash_flow.rb:537:10:537:19 | ( ... ) | -| hash_flow.rb:544:15:544:25 | call to taint : | hash_flow.rb:548:9:548:12 | hash [element :a] : | -| hash_flow.rb:546:15:546:25 | call to taint : | hash_flow.rb:548:9:548:12 | hash [element :c] : | -| hash_flow.rb:548:9:548:12 | [post] hash [element :a] : | hash_flow.rb:549:11:549:14 | hash [element :a] : | -| hash_flow.rb:548:9:548:12 | hash [element :a] : | hash_flow.rb:548:9:548:12 | [post] hash [element :a] : | -| hash_flow.rb:548:9:548:12 | hash [element :a] : | hash_flow.rb:548:9:548:18 | call to shift [element 1] : | -| hash_flow.rb:548:9:548:12 | hash [element :c] : | hash_flow.rb:548:9:548:18 | call to shift [element 1] : | -| hash_flow.rb:548:9:548:18 | call to shift [element 1] : | hash_flow.rb:551:11:551:11 | b [element 1] : | -| hash_flow.rb:549:11:549:14 | hash [element :a] : | hash_flow.rb:549:11:549:18 | ...[...] : | -| hash_flow.rb:549:11:549:18 | ...[...] : | hash_flow.rb:549:10:549:19 | ( ... ) | -| hash_flow.rb:551:11:551:11 | b [element 1] : | hash_flow.rb:551:11:551:14 | ...[...] : | -| hash_flow.rb:551:11:551:14 | ...[...] : | hash_flow.rb:551:10:551:15 | ( ... ) | -| hash_flow.rb:558:15:558:25 | call to taint : | hash_flow.rb:562:9:562:12 | hash [element :a] : | -| hash_flow.rb:558:15:558:25 | call to taint : | hash_flow.rb:567:9:567:12 | hash [element :a] : | -| hash_flow.rb:560:15:560:25 | call to taint : | hash_flow.rb:567:9:567:12 | hash [element :c] : | -| hash_flow.rb:562:9:562:12 | hash [element :a] : | hash_flow.rb:562:9:562:26 | call to slice [element :a] : | -| hash_flow.rb:562:9:562:26 | call to slice [element :a] : | hash_flow.rb:563:11:563:11 | b [element :a] : | -| hash_flow.rb:563:11:563:11 | b [element :a] : | hash_flow.rb:563:11:563:15 | ...[...] : | -| hash_flow.rb:563:11:563:15 | ...[...] : | hash_flow.rb:563:10:563:16 | ( ... ) | -| hash_flow.rb:567:9:567:12 | hash [element :a] : | hash_flow.rb:567:9:567:25 | call to slice [element :a] : | -| hash_flow.rb:567:9:567:12 | hash [element :c] : | hash_flow.rb:567:9:567:25 | call to slice [element :c] : | -| hash_flow.rb:567:9:567:25 | call to slice [element :a] : | hash_flow.rb:568:11:568:11 | c [element :a] : | -| hash_flow.rb:567:9:567:25 | call to slice [element :c] : | hash_flow.rb:570:11:570:11 | c [element :c] : | -| hash_flow.rb:568:11:568:11 | c [element :a] : | hash_flow.rb:568:11:568:15 | ...[...] : | -| hash_flow.rb:568:11:568:15 | ...[...] : | hash_flow.rb:568:10:568:16 | ( ... ) | -| hash_flow.rb:570:11:570:11 | c [element :c] : | hash_flow.rb:570:11:570:15 | ...[...] : | -| hash_flow.rb:570:11:570:15 | ...[...] : | hash_flow.rb:570:10:570:16 | ( ... ) | -| hash_flow.rb:577:15:577:25 | call to taint : | hash_flow.rb:581:9:581:12 | hash [element :a] : | -| hash_flow.rb:579:15:579:25 | call to taint : | hash_flow.rb:581:9:581:12 | hash [element :c] : | -| hash_flow.rb:581:9:581:12 | hash [element :a] : | hash_flow.rb:581:9:581:17 | call to to_a [element, element 1] : | -| hash_flow.rb:581:9:581:12 | hash [element :c] : | hash_flow.rb:581:9:581:17 | call to to_a [element, element 1] : | -| hash_flow.rb:581:9:581:17 | call to to_a [element, element 1] : | hash_flow.rb:583:11:583:11 | a [element, element 1] : | -| hash_flow.rb:583:11:583:11 | a [element, element 1] : | hash_flow.rb:583:11:583:14 | ...[...] [element 1] : | -| hash_flow.rb:583:11:583:14 | ...[...] [element 1] : | hash_flow.rb:583:11:583:17 | ...[...] : | -| hash_flow.rb:583:11:583:17 | ...[...] : | hash_flow.rb:583:10:583:18 | ( ... ) | -| hash_flow.rb:590:15:590:25 | call to taint : | hash_flow.rb:594:9:594:12 | hash [element :a] : | -| hash_flow.rb:590:15:590:25 | call to taint : | hash_flow.rb:599:9:599:12 | hash [element :a] : | -| hash_flow.rb:592:15:592:25 | call to taint : | hash_flow.rb:594:9:594:12 | hash [element :c] : | -| hash_flow.rb:592:15:592:25 | call to taint : | hash_flow.rb:599:9:599:12 | hash [element :c] : | -| hash_flow.rb:594:9:594:12 | hash [element :a] : | hash_flow.rb:594:9:594:17 | call to to_h [element :a] : | -| hash_flow.rb:594:9:594:12 | hash [element :c] : | hash_flow.rb:594:9:594:17 | call to to_h [element :c] : | -| hash_flow.rb:594:9:594:17 | call to to_h [element :a] : | hash_flow.rb:595:11:595:11 | a [element :a] : | -| hash_flow.rb:594:9:594:17 | call to to_h [element :c] : | hash_flow.rb:597:11:597:11 | a [element :c] : | -| hash_flow.rb:595:11:595:11 | a [element :a] : | hash_flow.rb:595:11:595:15 | ...[...] : | -| hash_flow.rb:595:11:595:15 | ...[...] : | hash_flow.rb:595:10:595:16 | ( ... ) | -| hash_flow.rb:597:11:597:11 | a [element :c] : | hash_flow.rb:597:11:597:15 | ...[...] : | -| hash_flow.rb:597:11:597:15 | ...[...] : | hash_flow.rb:597:10:597:16 | ( ... ) | -| hash_flow.rb:599:9:599:12 | hash [element :a] : | hash_flow.rb:599:28:599:32 | value : | -| hash_flow.rb:599:9:599:12 | hash [element :c] : | hash_flow.rb:599:28:599:32 | value : | -| hash_flow.rb:599:9:603:7 | call to to_h [element] : | hash_flow.rb:604:11:604:11 | b [element] : | -| hash_flow.rb:599:28:599:32 | value : | hash_flow.rb:601:14:601:18 | value | -| hash_flow.rb:602:14:602:24 | call to taint : | hash_flow.rb:599:9:603:7 | call to to_h [element] : | -| hash_flow.rb:604:11:604:11 | b [element] : | hash_flow.rb:604:11:604:15 | ...[...] : | -| hash_flow.rb:604:11:604:15 | ...[...] : | hash_flow.rb:604:10:604:16 | ( ... ) | -| hash_flow.rb:611:15:611:25 | call to taint : | hash_flow.rb:615:9:615:12 | hash [element :a] : | -| hash_flow.rb:613:15:613:25 | call to taint : | hash_flow.rb:615:9:615:12 | hash [element :c] : | -| hash_flow.rb:615:9:615:12 | hash [element :a] : | hash_flow.rb:615:9:615:45 | call to transform_keys [element] : | -| hash_flow.rb:615:9:615:12 | hash [element :c] : | hash_flow.rb:615:9:615:45 | call to transform_keys [element] : | -| hash_flow.rb:615:9:615:45 | call to transform_keys [element] : | hash_flow.rb:616:11:616:11 | a [element] : | -| hash_flow.rb:615:9:615:45 | call to transform_keys [element] : | hash_flow.rb:617:11:617:11 | a [element] : | -| hash_flow.rb:615:9:615:45 | call to transform_keys [element] : | hash_flow.rb:618:11:618:11 | a [element] : | -| hash_flow.rb:616:11:616:11 | a [element] : | hash_flow.rb:616:11:616:16 | ...[...] : | -| hash_flow.rb:616:11:616:16 | ...[...] : | hash_flow.rb:616:10:616:17 | ( ... ) | -| hash_flow.rb:617:11:617:11 | a [element] : | hash_flow.rb:617:11:617:16 | ...[...] : | -| hash_flow.rb:617:11:617:16 | ...[...] : | hash_flow.rb:617:10:617:17 | ( ... ) | -| hash_flow.rb:618:11:618:11 | a [element] : | hash_flow.rb:618:11:618:16 | ...[...] : | -| hash_flow.rb:618:11:618:16 | ...[...] : | hash_flow.rb:618:10:618:17 | ( ... ) | -| hash_flow.rb:625:15:625:25 | call to taint : | hash_flow.rb:629:5:629:8 | hash [element :a] : | -| hash_flow.rb:627:15:627:25 | call to taint : | hash_flow.rb:629:5:629:8 | hash [element :c] : | -| hash_flow.rb:629:5:629:8 | [post] hash [element] : | hash_flow.rb:630:11:630:14 | hash [element] : | -| hash_flow.rb:629:5:629:8 | [post] hash [element] : | hash_flow.rb:631:11:631:14 | hash [element] : | -| hash_flow.rb:629:5:629:8 | [post] hash [element] : | hash_flow.rb:632:11:632:14 | hash [element] : | -| hash_flow.rb:629:5:629:8 | hash [element :a] : | hash_flow.rb:629:5:629:8 | [post] hash [element] : | -| hash_flow.rb:629:5:629:8 | hash [element :c] : | hash_flow.rb:629:5:629:8 | [post] hash [element] : | -| hash_flow.rb:630:11:630:14 | hash [element] : | hash_flow.rb:630:11:630:19 | ...[...] : | -| hash_flow.rb:630:11:630:19 | ...[...] : | hash_flow.rb:630:10:630:20 | ( ... ) | -| hash_flow.rb:631:11:631:14 | hash [element] : | hash_flow.rb:631:11:631:19 | ...[...] : | -| hash_flow.rb:631:11:631:19 | ...[...] : | hash_flow.rb:631:10:631:20 | ( ... ) | -| hash_flow.rb:632:11:632:14 | hash [element] : | hash_flow.rb:632:11:632:19 | ...[...] : | -| hash_flow.rb:632:11:632:19 | ...[...] : | hash_flow.rb:632:10:632:20 | ( ... ) | -| hash_flow.rb:639:15:639:25 | call to taint : | hash_flow.rb:643:9:643:12 | hash [element :a] : | -| hash_flow.rb:639:15:639:25 | call to taint : | hash_flow.rb:647:11:647:14 | hash [element :a] : | -| hash_flow.rb:641:15:641:25 | call to taint : | hash_flow.rb:643:9:643:12 | hash [element :c] : | -| hash_flow.rb:643:9:643:12 | hash [element :a] : | hash_flow.rb:643:35:643:39 | value : | -| hash_flow.rb:643:9:643:12 | hash [element :c] : | hash_flow.rb:643:35:643:39 | value : | -| hash_flow.rb:643:9:646:7 | call to transform_values [element] : | hash_flow.rb:648:11:648:11 | b [element] : | -| hash_flow.rb:643:35:643:39 | value : | hash_flow.rb:644:14:644:18 | value | -| hash_flow.rb:645:9:645:19 | call to taint : | hash_flow.rb:643:9:646:7 | call to transform_values [element] : | -| hash_flow.rb:647:11:647:14 | hash [element :a] : | hash_flow.rb:647:11:647:18 | ...[...] : | -| hash_flow.rb:647:11:647:18 | ...[...] : | hash_flow.rb:647:10:647:19 | ( ... ) | -| hash_flow.rb:648:11:648:11 | b [element] : | hash_flow.rb:648:11:648:15 | ...[...] : | -| hash_flow.rb:648:11:648:15 | ...[...] : | hash_flow.rb:648:10:648:16 | ( ... ) | -| hash_flow.rb:655:15:655:25 | call to taint : | hash_flow.rb:659:5:659:8 | hash [element :a] : | -| hash_flow.rb:657:15:657:25 | call to taint : | hash_flow.rb:659:5:659:8 | hash [element :c] : | -| hash_flow.rb:659:5:659:8 | [post] hash [element] : | hash_flow.rb:663:11:663:14 | hash [element] : | -| hash_flow.rb:659:5:659:8 | hash [element :a] : | hash_flow.rb:659:32:659:36 | value : | -| hash_flow.rb:659:5:659:8 | hash [element :c] : | hash_flow.rb:659:32:659:36 | value : | -| hash_flow.rb:659:32:659:36 | value : | hash_flow.rb:660:14:660:18 | value | -| hash_flow.rb:661:9:661:19 | call to taint : | hash_flow.rb:659:5:659:8 | [post] hash [element] : | -| hash_flow.rb:663:11:663:14 | hash [element] : | hash_flow.rb:663:11:663:18 | ...[...] : | -| hash_flow.rb:663:11:663:18 | ...[...] : | hash_flow.rb:663:10:663:19 | ( ... ) | -| hash_flow.rb:670:15:670:25 | call to taint : | hash_flow.rb:679:12:679:16 | hash1 [element :a] : | -| hash_flow.rb:672:15:672:25 | call to taint : | hash_flow.rb:679:12:679:16 | hash1 [element :c] : | -| hash_flow.rb:675:15:675:25 | call to taint : | hash_flow.rb:679:25:679:29 | hash2 [element :d] : | -| hash_flow.rb:677:15:677:25 | call to taint : | hash_flow.rb:679:25:679:29 | hash2 [element :f] : | -| hash_flow.rb:679:12:679:16 | [post] hash1 [element :a] : | hash_flow.rb:691:11:691:15 | hash1 [element :a] : | -| hash_flow.rb:679:12:679:16 | [post] hash1 [element :c] : | hash_flow.rb:693:11:693:15 | hash1 [element :c] : | -| hash_flow.rb:679:12:679:16 | [post] hash1 [element :d] : | hash_flow.rb:694:11:694:15 | hash1 [element :d] : | -| hash_flow.rb:679:12:679:16 | [post] hash1 [element :f] : | hash_flow.rb:696:11:696:15 | hash1 [element :f] : | -| hash_flow.rb:679:12:679:16 | hash1 [element :a] : | hash_flow.rb:679:12:679:16 | [post] hash1 [element :a] : | -| hash_flow.rb:679:12:679:16 | hash1 [element :a] : | hash_flow.rb:679:12:683:7 | call to update [element :a] : | -| hash_flow.rb:679:12:679:16 | hash1 [element :a] : | hash_flow.rb:679:41:679:49 | old_value : | -| hash_flow.rb:679:12:679:16 | hash1 [element :a] : | hash_flow.rb:679:52:679:60 | new_value : | -| hash_flow.rb:679:12:679:16 | hash1 [element :c] : | hash_flow.rb:679:12:679:16 | [post] hash1 [element :c] : | -| hash_flow.rb:679:12:679:16 | hash1 [element :c] : | hash_flow.rb:679:12:683:7 | call to update [element :c] : | -| hash_flow.rb:679:12:679:16 | hash1 [element :c] : | hash_flow.rb:679:41:679:49 | old_value : | -| hash_flow.rb:679:12:679:16 | hash1 [element :c] : | hash_flow.rb:679:52:679:60 | new_value : | -| hash_flow.rb:679:12:683:7 | call to update [element :a] : | hash_flow.rb:684:11:684:14 | hash [element :a] : | -| hash_flow.rb:679:12:683:7 | call to update [element :c] : | hash_flow.rb:686:11:686:14 | hash [element :c] : | -| hash_flow.rb:679:12:683:7 | call to update [element :d] : | hash_flow.rb:687:11:687:14 | hash [element :d] : | -| hash_flow.rb:679:12:683:7 | call to update [element :f] : | hash_flow.rb:689:11:689:14 | hash [element :f] : | -| hash_flow.rb:679:25:679:29 | hash2 [element :d] : | hash_flow.rb:679:12:679:16 | [post] hash1 [element :d] : | -| hash_flow.rb:679:25:679:29 | hash2 [element :d] : | hash_flow.rb:679:12:683:7 | call to update [element :d] : | -| hash_flow.rb:679:25:679:29 | hash2 [element :d] : | hash_flow.rb:679:41:679:49 | old_value : | -| hash_flow.rb:679:25:679:29 | hash2 [element :d] : | hash_flow.rb:679:52:679:60 | new_value : | -| hash_flow.rb:679:25:679:29 | hash2 [element :f] : | hash_flow.rb:679:12:679:16 | [post] hash1 [element :f] : | -| hash_flow.rb:679:25:679:29 | hash2 [element :f] : | hash_flow.rb:679:12:683:7 | call to update [element :f] : | -| hash_flow.rb:679:25:679:29 | hash2 [element :f] : | hash_flow.rb:679:41:679:49 | old_value : | -| hash_flow.rb:679:25:679:29 | hash2 [element :f] : | hash_flow.rb:679:52:679:60 | new_value : | -| hash_flow.rb:679:41:679:49 | old_value : | hash_flow.rb:681:14:681:22 | old_value | -| hash_flow.rb:679:52:679:60 | new_value : | hash_flow.rb:682:14:682:22 | new_value | -| hash_flow.rb:684:11:684:14 | hash [element :a] : | hash_flow.rb:684:11:684:18 | ...[...] : | -| hash_flow.rb:684:11:684:18 | ...[...] : | hash_flow.rb:684:10:684:19 | ( ... ) | -| hash_flow.rb:686:11:686:14 | hash [element :c] : | hash_flow.rb:686:11:686:18 | ...[...] : | -| hash_flow.rb:686:11:686:18 | ...[...] : | hash_flow.rb:686:10:686:19 | ( ... ) | -| hash_flow.rb:687:11:687:14 | hash [element :d] : | hash_flow.rb:687:11:687:18 | ...[...] : | -| hash_flow.rb:687:11:687:18 | ...[...] : | hash_flow.rb:687:10:687:19 | ( ... ) | -| hash_flow.rb:689:11:689:14 | hash [element :f] : | hash_flow.rb:689:11:689:18 | ...[...] : | -| hash_flow.rb:689:11:689:18 | ...[...] : | hash_flow.rb:689:10:689:19 | ( ... ) | -| hash_flow.rb:691:11:691:15 | hash1 [element :a] : | hash_flow.rb:691:11:691:19 | ...[...] : | -| hash_flow.rb:691:11:691:19 | ...[...] : | hash_flow.rb:691:10:691:20 | ( ... ) | -| hash_flow.rb:693:11:693:15 | hash1 [element :c] : | hash_flow.rb:693:11:693:19 | ...[...] : | -| hash_flow.rb:693:11:693:19 | ...[...] : | hash_flow.rb:693:10:693:20 | ( ... ) | -| hash_flow.rb:694:11:694:15 | hash1 [element :d] : | hash_flow.rb:694:11:694:19 | ...[...] : | -| hash_flow.rb:694:11:694:19 | ...[...] : | hash_flow.rb:694:10:694:20 | ( ... ) | -| hash_flow.rb:696:11:696:15 | hash1 [element :f] : | hash_flow.rb:696:11:696:19 | ...[...] : | -| hash_flow.rb:696:11:696:19 | ...[...] : | hash_flow.rb:696:10:696:20 | ( ... ) | -| hash_flow.rb:703:15:703:25 | call to taint : | hash_flow.rb:707:9:707:12 | hash [element :a] : | -| hash_flow.rb:705:15:705:25 | call to taint : | hash_flow.rb:707:9:707:12 | hash [element :c] : | -| hash_flow.rb:707:9:707:12 | hash [element :a] : | hash_flow.rb:707:9:707:19 | call to values [element] : | -| hash_flow.rb:707:9:707:12 | hash [element :c] : | hash_flow.rb:707:9:707:19 | call to values [element] : | -| hash_flow.rb:707:9:707:19 | call to values [element] : | hash_flow.rb:708:11:708:11 | a [element] : | -| hash_flow.rb:708:11:708:11 | a [element] : | hash_flow.rb:708:11:708:14 | ...[...] : | -| hash_flow.rb:708:11:708:14 | ...[...] : | hash_flow.rb:708:10:708:15 | ( ... ) | -| hash_flow.rb:715:15:715:25 | call to taint : | hash_flow.rb:719:9:719:12 | hash [element :a] : | -| hash_flow.rb:715:15:715:25 | call to taint : | hash_flow.rb:721:9:721:12 | hash [element :a] : | -| hash_flow.rb:717:15:717:25 | call to taint : | hash_flow.rb:721:9:721:12 | hash [element :c] : | -| hash_flow.rb:719:9:719:12 | hash [element :a] : | hash_flow.rb:719:9:719:26 | call to values_at [element 0] : | -| hash_flow.rb:719:9:719:26 | call to values_at [element 0] : | hash_flow.rb:720:10:720:10 | b [element 0] : | -| hash_flow.rb:720:10:720:10 | b [element 0] : | hash_flow.rb:720:10:720:13 | ...[...] | -| hash_flow.rb:721:9:721:12 | hash [element :a] : | hash_flow.rb:721:9:721:31 | call to fetch_values [element] : | -| hash_flow.rb:721:9:721:12 | hash [element :c] : | hash_flow.rb:721:9:721:31 | call to fetch_values [element] : | -| hash_flow.rb:721:9:721:31 | call to fetch_values [element] : | hash_flow.rb:722:10:722:10 | b [element] : | -| hash_flow.rb:722:10:722:10 | b [element] : | hash_flow.rb:722:10:722:13 | ...[...] | -| hash_flow.rb:729:15:729:25 | call to taint : | hash_flow.rb:738:16:738:20 | hash1 [element :a] : | -| hash_flow.rb:731:15:731:25 | call to taint : | hash_flow.rb:738:16:738:20 | hash1 [element :c] : | -| hash_flow.rb:734:15:734:25 | call to taint : | hash_flow.rb:738:44:738:48 | hash2 [element :d] : | -| hash_flow.rb:736:15:736:25 | call to taint : | hash_flow.rb:738:44:738:48 | hash2 [element :f] : | -| hash_flow.rb:738:14:738:20 | ** ... [element :a] : | hash_flow.rb:739:10:739:13 | hash [element :a] : | -| hash_flow.rb:738:14:738:20 | ** ... [element :c] : | hash_flow.rb:741:10:741:13 | hash [element :c] : | -| hash_flow.rb:738:16:738:20 | hash1 [element :a] : | hash_flow.rb:738:14:738:20 | ** ... [element :a] : | -| hash_flow.rb:738:16:738:20 | hash1 [element :c] : | hash_flow.rb:738:14:738:20 | ** ... [element :c] : | -| hash_flow.rb:738:29:738:39 | call to taint : | hash_flow.rb:745:10:745:13 | hash [element :g] : | -| hash_flow.rb:738:42:738:48 | ** ... [element :d] : | hash_flow.rb:742:10:742:13 | hash [element :d] : | -| hash_flow.rb:738:42:738:48 | ** ... [element :f] : | hash_flow.rb:744:10:744:13 | hash [element :f] : | -| hash_flow.rb:738:44:738:48 | hash2 [element :d] : | hash_flow.rb:738:42:738:48 | ** ... [element :d] : | -| hash_flow.rb:738:44:738:48 | hash2 [element :f] : | hash_flow.rb:738:42:738:48 | ** ... [element :f] : | -| hash_flow.rb:739:10:739:13 | hash [element :a] : | hash_flow.rb:739:10:739:17 | ...[...] | -| hash_flow.rb:741:10:741:13 | hash [element :c] : | hash_flow.rb:741:10:741:17 | ...[...] | -| hash_flow.rb:742:10:742:13 | hash [element :d] : | hash_flow.rb:742:10:742:17 | ...[...] | -| hash_flow.rb:744:10:744:13 | hash [element :f] : | hash_flow.rb:744:10:744:17 | ...[...] | -| hash_flow.rb:745:10:745:13 | hash [element :g] : | hash_flow.rb:745:10:745:17 | ...[...] | +| hash_flow.rb:72:13:72:45 | ...[...] [element a] : | hash_flow.rb:73:10:73:14 | hash5 [element a] : | +| hash_flow.rb:72:18:72:34 | Pair [pair a] : | hash_flow.rb:72:13:72:45 | ...[...] [element a] : | +| hash_flow.rb:72:25:72:34 | call to taint : | hash_flow.rb:72:18:72:34 | Pair [pair a] : | +| hash_flow.rb:73:10:73:14 | hash5 [element a] : | hash_flow.rb:73:10:73:19 | ...[...] | +| hash_flow.rb:76:13:76:47 | ...[...] [element a] : | hash_flow.rb:77:10:77:14 | hash6 [element a] : | +| hash_flow.rb:76:19:76:35 | Pair [pair a] : | hash_flow.rb:76:13:76:47 | ...[...] [element a] : | +| hash_flow.rb:76:26:76:35 | call to taint : | hash_flow.rb:76:19:76:35 | Pair [pair a] : | +| hash_flow.rb:77:10:77:14 | hash6 [element a] : | hash_flow.rb:77:10:77:19 | ...[...] | +| hash_flow.rb:84:13:84:42 | call to [] [element :a] : | hash_flow.rb:85:10:85:14 | hash1 [element :a] : | +| hash_flow.rb:84:26:84:35 | call to taint : | hash_flow.rb:84:13:84:42 | call to [] [element :a] : | +| hash_flow.rb:85:10:85:14 | hash1 [element :a] : | hash_flow.rb:85:10:85:18 | ...[...] | +| hash_flow.rb:93:15:93:24 | call to taint : | hash_flow.rb:96:30:96:33 | hash [element :a] : | +| hash_flow.rb:96:13:96:34 | call to try_convert [element :a] : | hash_flow.rb:97:10:97:14 | hash2 [element :a] : | +| hash_flow.rb:96:30:96:33 | hash [element :a] : | hash_flow.rb:96:13:96:34 | call to try_convert [element :a] : | +| hash_flow.rb:97:10:97:14 | hash2 [element :a] : | hash_flow.rb:97:10:97:18 | ...[...] | +| hash_flow.rb:105:21:105:30 | call to taint : | hash_flow.rb:106:10:106:10 | b | +| hash_flow.rb:113:9:113:12 | [post] hash [element :a] : | hash_flow.rb:114:10:114:13 | hash [element :a] : | +| hash_flow.rb:113:9:113:34 | call to store : | hash_flow.rb:115:10:115:10 | b | +| hash_flow.rb:113:24:113:33 | call to taint : | hash_flow.rb:113:9:113:12 | [post] hash [element :a] : | +| hash_flow.rb:113:24:113:33 | call to taint : | hash_flow.rb:113:9:113:34 | call to store : | +| hash_flow.rb:114:10:114:13 | hash [element :a] : | hash_flow.rb:114:10:114:17 | ...[...] | +| hash_flow.rb:118:9:118:12 | [post] hash [element] : | hash_flow.rb:119:10:119:13 | hash [element] : | +| hash_flow.rb:118:9:118:12 | [post] hash [element] : | hash_flow.rb:120:10:120:13 | hash [element] : | +| hash_flow.rb:118:9:118:33 | call to store : | hash_flow.rb:121:10:121:10 | c | +| hash_flow.rb:118:23:118:32 | call to taint : | hash_flow.rb:118:9:118:12 | [post] hash [element] : | +| hash_flow.rb:118:23:118:32 | call to taint : | hash_flow.rb:118:9:118:33 | call to store : | +| hash_flow.rb:119:10:119:13 | hash [element] : | hash_flow.rb:119:10:119:17 | ...[...] | +| hash_flow.rb:120:10:120:13 | hash [element] : | hash_flow.rb:120:10:120:17 | ...[...] | +| hash_flow.rb:128:15:128:24 | call to taint : | hash_flow.rb:131:5:131:8 | hash [element :a] : | +| hash_flow.rb:128:15:128:24 | call to taint : | hash_flow.rb:134:5:134:8 | hash [element :a] : | +| hash_flow.rb:131:5:131:8 | hash [element :a] : | hash_flow.rb:131:18:131:29 | key_or_value : | +| hash_flow.rb:131:18:131:29 | key_or_value : | hash_flow.rb:132:14:132:25 | key_or_value | +| hash_flow.rb:134:5:134:8 | hash [element :a] : | hash_flow.rb:134:22:134:26 | value : | +| hash_flow.rb:134:22:134:26 | value : | hash_flow.rb:136:14:136:18 | value | +| hash_flow.rb:144:15:144:25 | call to taint : | hash_flow.rb:147:9:147:12 | hash [element :a] : | +| hash_flow.rb:144:15:144:25 | call to taint : | hash_flow.rb:151:9:151:12 | hash [element :a] : | +| hash_flow.rb:147:9:147:12 | hash [element :a] : | hash_flow.rb:147:9:147:22 | call to assoc [element 1] : | +| hash_flow.rb:147:9:147:22 | call to assoc [element 1] : | hash_flow.rb:149:10:149:10 | b [element 1] : | +| hash_flow.rb:147:9:147:22 | call to assoc [element 1] : | hash_flow.rb:150:10:150:10 | b [element 1] : | +| hash_flow.rb:149:10:149:10 | b [element 1] : | hash_flow.rb:149:10:149:13 | ...[...] | +| hash_flow.rb:150:10:150:10 | b [element 1] : | hash_flow.rb:150:10:150:13 | ...[...] | +| hash_flow.rb:151:9:151:12 | hash [element :a] : | hash_flow.rb:151:9:151:21 | call to assoc [element 1] : | +| hash_flow.rb:151:9:151:21 | call to assoc [element 1] : | hash_flow.rb:152:10:152:10 | c [element 1] : | +| hash_flow.rb:152:10:152:10 | c [element 1] : | hash_flow.rb:152:10:152:13 | ...[...] | +| hash_flow.rb:170:15:170:25 | call to taint : | hash_flow.rb:173:9:173:12 | hash [element :a] : | +| hash_flow.rb:173:9:173:12 | hash [element :a] : | hash_flow.rb:173:9:173:20 | call to compact [element :a] : | +| hash_flow.rb:173:9:173:20 | call to compact [element :a] : | hash_flow.rb:174:10:174:10 | a [element :a] : | +| hash_flow.rb:174:10:174:10 | a [element :a] : | hash_flow.rb:174:10:174:14 | ...[...] | +| hash_flow.rb:182:15:182:25 | call to taint : | hash_flow.rb:185:9:185:12 | hash [element :a] : | +| hash_flow.rb:185:9:185:12 | hash [element :a] : | hash_flow.rb:185:9:185:23 | call to delete : | +| hash_flow.rb:185:9:185:23 | call to delete : | hash_flow.rb:186:10:186:10 | a | +| hash_flow.rb:194:15:194:25 | call to taint : | hash_flow.rb:197:9:197:12 | hash [element :a] : | +| hash_flow.rb:194:15:194:25 | call to taint : | hash_flow.rb:202:10:202:13 | hash [element :a] : | +| hash_flow.rb:197:9:197:12 | [post] hash [element :a] : | hash_flow.rb:202:10:202:13 | hash [element :a] : | +| hash_flow.rb:197:9:197:12 | hash [element :a] : | hash_flow.rb:197:9:197:12 | [post] hash [element :a] : | +| hash_flow.rb:197:9:197:12 | hash [element :a] : | hash_flow.rb:197:9:200:7 | call to delete_if [element :a] : | +| hash_flow.rb:197:9:197:12 | hash [element :a] : | hash_flow.rb:197:33:197:37 | value : | +| hash_flow.rb:197:9:200:7 | call to delete_if [element :a] : | hash_flow.rb:201:10:201:10 | a [element :a] : | +| hash_flow.rb:197:33:197:37 | value : | hash_flow.rb:199:14:199:18 | value | +| hash_flow.rb:201:10:201:10 | a [element :a] : | hash_flow.rb:201:10:201:14 | ...[...] | +| hash_flow.rb:202:10:202:13 | hash [element :a] : | hash_flow.rb:202:10:202:17 | ...[...] | +| hash_flow.rb:210:15:210:25 | call to taint : | hash_flow.rb:217:10:217:13 | hash [element :a] : | +| hash_flow.rb:213:19:213:29 | call to taint : | hash_flow.rb:219:10:219:13 | hash [element :c, element :d] : | +| hash_flow.rb:217:10:217:13 | hash [element :a] : | hash_flow.rb:217:10:217:21 | call to dig | +| hash_flow.rb:219:10:219:13 | hash [element :c, element :d] : | hash_flow.rb:219:10:219:24 | call to dig | +| hash_flow.rb:227:15:227:25 | call to taint : | hash_flow.rb:230:9:230:12 | hash [element :a] : | +| hash_flow.rb:230:9:230:12 | hash [element :a] : | hash_flow.rb:230:9:233:7 | call to each [element :a] : | +| hash_flow.rb:230:9:230:12 | hash [element :a] : | hash_flow.rb:230:28:230:32 | value : | +| hash_flow.rb:230:9:233:7 | call to each [element :a] : | hash_flow.rb:234:10:234:10 | x [element :a] : | +| hash_flow.rb:230:28:230:32 | value : | hash_flow.rb:232:14:232:18 | value | +| hash_flow.rb:234:10:234:10 | x [element :a] : | hash_flow.rb:234:10:234:14 | ...[...] | +| hash_flow.rb:242:15:242:25 | call to taint : | hash_flow.rb:245:9:245:12 | hash [element :a] : | +| hash_flow.rb:245:9:245:12 | hash [element :a] : | hash_flow.rb:245:9:247:7 | call to each_key [element :a] : | +| hash_flow.rb:245:9:247:7 | call to each_key [element :a] : | hash_flow.rb:248:10:248:10 | x [element :a] : | +| hash_flow.rb:248:10:248:10 | x [element :a] : | hash_flow.rb:248:10:248:14 | ...[...] | +| hash_flow.rb:256:15:256:25 | call to taint : | hash_flow.rb:259:9:259:12 | hash [element :a] : | +| hash_flow.rb:259:9:259:12 | hash [element :a] : | hash_flow.rb:259:9:262:7 | call to each_pair [element :a] : | +| hash_flow.rb:259:9:259:12 | hash [element :a] : | hash_flow.rb:259:33:259:37 | value : | +| hash_flow.rb:259:9:262:7 | call to each_pair [element :a] : | hash_flow.rb:263:10:263:10 | x [element :a] : | +| hash_flow.rb:259:33:259:37 | value : | hash_flow.rb:261:14:261:18 | value | +| hash_flow.rb:263:10:263:10 | x [element :a] : | hash_flow.rb:263:10:263:14 | ...[...] | +| hash_flow.rb:271:15:271:25 | call to taint : | hash_flow.rb:274:9:274:12 | hash [element :a] : | +| hash_flow.rb:274:9:274:12 | hash [element :a] : | hash_flow.rb:274:9:276:7 | call to each_value [element :a] : | +| hash_flow.rb:274:9:274:12 | hash [element :a] : | hash_flow.rb:274:29:274:33 | value : | +| hash_flow.rb:274:9:276:7 | call to each_value [element :a] : | hash_flow.rb:277:10:277:10 | x [element :a] : | +| hash_flow.rb:274:29:274:33 | value : | hash_flow.rb:275:14:275:18 | value | +| hash_flow.rb:277:10:277:10 | x [element :a] : | hash_flow.rb:277:10:277:14 | ...[...] | +| hash_flow.rb:287:15:287:25 | call to taint : | hash_flow.rb:290:9:290:12 | hash [element :c] : | +| hash_flow.rb:290:9:290:12 | hash [element :c] : | hash_flow.rb:290:9:290:28 | call to except [element :c] : | +| hash_flow.rb:290:9:290:28 | call to except [element :c] : | hash_flow.rb:293:10:293:10 | x [element :c] : | +| hash_flow.rb:293:10:293:10 | x [element :c] : | hash_flow.rb:293:10:293:14 | ...[...] | +| hash_flow.rb:301:15:301:25 | call to taint : | hash_flow.rb:305:9:305:12 | hash [element :a] : | +| hash_flow.rb:301:15:301:25 | call to taint : | hash_flow.rb:309:9:309:12 | hash [element :a] : | +| hash_flow.rb:301:15:301:25 | call to taint : | hash_flow.rb:311:9:311:12 | hash [element :a] : | +| hash_flow.rb:301:15:301:25 | call to taint : | hash_flow.rb:315:9:315:12 | hash [element :a] : | +| hash_flow.rb:303:15:303:25 | call to taint : | hash_flow.rb:305:9:305:12 | hash [element :c] : | +| hash_flow.rb:303:15:303:25 | call to taint : | hash_flow.rb:315:9:315:12 | hash [element :c] : | +| hash_flow.rb:305:9:305:12 | hash [element :a] : | hash_flow.rb:305:9:307:7 | call to fetch : | +| hash_flow.rb:305:9:305:12 | hash [element :c] : | hash_flow.rb:305:9:307:7 | call to fetch : | +| hash_flow.rb:305:9:307:7 | call to fetch : | hash_flow.rb:308:10:308:10 | b | +| hash_flow.rb:305:20:305:30 | call to taint : | hash_flow.rb:305:37:305:37 | x : | +| hash_flow.rb:305:37:305:37 | x : | hash_flow.rb:306:14:306:14 | x | +| hash_flow.rb:309:9:309:12 | hash [element :a] : | hash_flow.rb:309:9:309:22 | call to fetch : | +| hash_flow.rb:309:9:309:22 | call to fetch : | hash_flow.rb:310:10:310:10 | b | +| hash_flow.rb:311:9:311:12 | hash [element :a] : | hash_flow.rb:311:9:311:35 | call to fetch : | +| hash_flow.rb:311:9:311:35 | call to fetch : | hash_flow.rb:312:10:312:10 | b | +| hash_flow.rb:311:24:311:34 | call to taint : | hash_flow.rb:311:9:311:35 | call to fetch : | +| hash_flow.rb:313:9:313:35 | call to fetch : | hash_flow.rb:314:10:314:10 | b | +| hash_flow.rb:313:24:313:34 | call to taint : | hash_flow.rb:313:9:313:35 | call to fetch : | +| hash_flow.rb:315:9:315:12 | hash [element :a] : | hash_flow.rb:315:9:315:34 | call to fetch : | +| hash_flow.rb:315:9:315:12 | hash [element :c] : | hash_flow.rb:315:9:315:34 | call to fetch : | +| hash_flow.rb:315:9:315:34 | call to fetch : | hash_flow.rb:316:10:316:10 | b | +| hash_flow.rb:315:23:315:33 | call to taint : | hash_flow.rb:315:9:315:34 | call to fetch : | +| hash_flow.rb:323:15:323:25 | call to taint : | hash_flow.rb:327:9:327:12 | hash [element :a] : | +| hash_flow.rb:323:15:323:25 | call to taint : | hash_flow.rb:332:9:332:12 | hash [element :a] : | +| hash_flow.rb:323:15:323:25 | call to taint : | hash_flow.rb:334:9:334:12 | hash [element :a] : | +| hash_flow.rb:325:15:325:25 | call to taint : | hash_flow.rb:327:9:327:12 | hash [element :c] : | +| hash_flow.rb:325:15:325:25 | call to taint : | hash_flow.rb:334:9:334:12 | hash [element :c] : | +| hash_flow.rb:327:9:327:12 | hash [element :a] : | hash_flow.rb:327:9:330:7 | call to fetch_values [element] : | +| hash_flow.rb:327:9:327:12 | hash [element :c] : | hash_flow.rb:327:9:330:7 | call to fetch_values [element] : | +| hash_flow.rb:327:9:330:7 | call to fetch_values [element] : | hash_flow.rb:331:10:331:10 | b [element] : | +| hash_flow.rb:327:27:327:37 | call to taint : | hash_flow.rb:327:44:327:44 | x : | +| hash_flow.rb:327:44:327:44 | x : | hash_flow.rb:328:14:328:14 | x | +| hash_flow.rb:329:9:329:19 | call to taint : | hash_flow.rb:327:9:330:7 | call to fetch_values [element] : | +| hash_flow.rb:331:10:331:10 | b [element] : | hash_flow.rb:331:10:331:13 | ...[...] | +| hash_flow.rb:332:9:332:12 | hash [element :a] : | hash_flow.rb:332:9:332:29 | call to fetch_values [element] : | +| hash_flow.rb:332:9:332:29 | call to fetch_values [element] : | hash_flow.rb:333:10:333:10 | b [element] : | +| hash_flow.rb:333:10:333:10 | b [element] : | hash_flow.rb:333:10:333:13 | ...[...] | +| hash_flow.rb:334:9:334:12 | hash [element :a] : | hash_flow.rb:334:9:334:31 | call to fetch_values [element] : | +| hash_flow.rb:334:9:334:12 | hash [element :c] : | hash_flow.rb:334:9:334:31 | call to fetch_values [element] : | +| hash_flow.rb:334:9:334:31 | call to fetch_values [element] : | hash_flow.rb:335:10:335:10 | b [element] : | +| hash_flow.rb:335:10:335:10 | b [element] : | hash_flow.rb:335:10:335:13 | ...[...] | +| hash_flow.rb:342:15:342:25 | call to taint : | hash_flow.rb:346:9:346:12 | hash [element :a] : | +| hash_flow.rb:344:15:344:25 | call to taint : | hash_flow.rb:346:9:346:12 | hash [element :c] : | +| hash_flow.rb:346:9:346:12 | hash [element :a] : | hash_flow.rb:346:9:350:7 | call to filter [element :a] : | +| hash_flow.rb:346:9:346:12 | hash [element :a] : | hash_flow.rb:346:30:346:34 | value : | +| hash_flow.rb:346:9:346:12 | hash [element :c] : | hash_flow.rb:346:30:346:34 | value : | +| hash_flow.rb:346:9:350:7 | call to filter [element :a] : | hash_flow.rb:351:11:351:11 | b [element :a] : | +| hash_flow.rb:346:30:346:34 | value : | hash_flow.rb:348:14:348:18 | value | +| hash_flow.rb:351:11:351:11 | b [element :a] : | hash_flow.rb:351:11:351:15 | ...[...] : | +| hash_flow.rb:351:11:351:15 | ...[...] : | hash_flow.rb:351:10:351:16 | ( ... ) | +| hash_flow.rb:358:15:358:25 | call to taint : | hash_flow.rb:362:5:362:8 | hash [element :a] : | +| hash_flow.rb:360:15:360:25 | call to taint : | hash_flow.rb:362:5:362:8 | hash [element :c] : | +| hash_flow.rb:362:5:362:8 | [post] hash [element :a] : | hash_flow.rb:367:11:367:14 | hash [element :a] : | +| hash_flow.rb:362:5:362:8 | hash [element :a] : | hash_flow.rb:362:5:362:8 | [post] hash [element :a] : | +| hash_flow.rb:362:5:362:8 | hash [element :a] : | hash_flow.rb:362:27:362:31 | value : | +| hash_flow.rb:362:5:362:8 | hash [element :c] : | hash_flow.rb:362:27:362:31 | value : | +| hash_flow.rb:362:27:362:31 | value : | hash_flow.rb:364:14:364:18 | value | +| hash_flow.rb:367:11:367:14 | hash [element :a] : | hash_flow.rb:367:11:367:18 | ...[...] : | +| hash_flow.rb:367:11:367:18 | ...[...] : | hash_flow.rb:367:10:367:19 | ( ... ) | +| hash_flow.rb:374:15:374:25 | call to taint : | hash_flow.rb:378:9:378:12 | hash [element :a] : | +| hash_flow.rb:376:15:376:25 | call to taint : | hash_flow.rb:378:9:378:12 | hash [element :c] : | +| hash_flow.rb:378:9:378:12 | hash [element :a] : | hash_flow.rb:378:9:378:20 | call to flatten [element] : | +| hash_flow.rb:378:9:378:12 | hash [element :c] : | hash_flow.rb:378:9:378:20 | call to flatten [element] : | +| hash_flow.rb:378:9:378:20 | call to flatten [element] : | hash_flow.rb:379:11:379:11 | b [element] : | +| hash_flow.rb:379:11:379:11 | b [element] : | hash_flow.rb:379:11:379:14 | ...[...] : | +| hash_flow.rb:379:11:379:14 | ...[...] : | hash_flow.rb:379:10:379:15 | ( ... ) | +| hash_flow.rb:386:15:386:25 | call to taint : | hash_flow.rb:390:9:390:12 | hash [element :a] : | +| hash_flow.rb:388:15:388:25 | call to taint : | hash_flow.rb:390:9:390:12 | hash [element :c] : | +| hash_flow.rb:390:9:390:12 | [post] hash [element :a] : | hash_flow.rb:395:11:395:14 | hash [element :a] : | +| hash_flow.rb:390:9:390:12 | hash [element :a] : | hash_flow.rb:390:9:390:12 | [post] hash [element :a] : | +| hash_flow.rb:390:9:390:12 | hash [element :a] : | hash_flow.rb:390:9:394:7 | call to keep_if [element :a] : | +| hash_flow.rb:390:9:390:12 | hash [element :a] : | hash_flow.rb:390:31:390:35 | value : | +| hash_flow.rb:390:9:390:12 | hash [element :c] : | hash_flow.rb:390:31:390:35 | value : | +| hash_flow.rb:390:9:394:7 | call to keep_if [element :a] : | hash_flow.rb:396:11:396:11 | b [element :a] : | +| hash_flow.rb:390:31:390:35 | value : | hash_flow.rb:392:14:392:18 | value | +| hash_flow.rb:395:11:395:14 | hash [element :a] : | hash_flow.rb:395:11:395:18 | ...[...] : | +| hash_flow.rb:395:11:395:18 | ...[...] : | hash_flow.rb:395:10:395:19 | ( ... ) | +| hash_flow.rb:396:11:396:11 | b [element :a] : | hash_flow.rb:396:11:396:15 | ...[...] : | +| hash_flow.rb:396:11:396:15 | ...[...] : | hash_flow.rb:396:10:396:16 | ( ... ) | +| hash_flow.rb:403:15:403:25 | call to taint : | hash_flow.rb:412:12:412:16 | hash1 [element :a] : | +| hash_flow.rb:405:15:405:25 | call to taint : | hash_flow.rb:412:12:412:16 | hash1 [element :c] : | +| hash_flow.rb:408:15:408:25 | call to taint : | hash_flow.rb:412:24:412:28 | hash2 [element :d] : | +| hash_flow.rb:410:15:410:25 | call to taint : | hash_flow.rb:412:24:412:28 | hash2 [element :f] : | +| hash_flow.rb:412:12:412:16 | hash1 [element :a] : | hash_flow.rb:412:12:416:7 | call to merge [element :a] : | +| hash_flow.rb:412:12:412:16 | hash1 [element :a] : | hash_flow.rb:412:40:412:48 | old_value : | +| hash_flow.rb:412:12:412:16 | hash1 [element :a] : | hash_flow.rb:412:51:412:59 | new_value : | +| hash_flow.rb:412:12:412:16 | hash1 [element :c] : | hash_flow.rb:412:12:416:7 | call to merge [element :c] : | +| hash_flow.rb:412:12:412:16 | hash1 [element :c] : | hash_flow.rb:412:40:412:48 | old_value : | +| hash_flow.rb:412:12:412:16 | hash1 [element :c] : | hash_flow.rb:412:51:412:59 | new_value : | +| hash_flow.rb:412:12:416:7 | call to merge [element :a] : | hash_flow.rb:417:11:417:14 | hash [element :a] : | +| hash_flow.rb:412:12:416:7 | call to merge [element :c] : | hash_flow.rb:419:11:419:14 | hash [element :c] : | +| hash_flow.rb:412:12:416:7 | call to merge [element :d] : | hash_flow.rb:420:11:420:14 | hash [element :d] : | +| hash_flow.rb:412:12:416:7 | call to merge [element :f] : | hash_flow.rb:422:11:422:14 | hash [element :f] : | +| hash_flow.rb:412:24:412:28 | hash2 [element :d] : | hash_flow.rb:412:12:416:7 | call to merge [element :d] : | +| hash_flow.rb:412:24:412:28 | hash2 [element :d] : | hash_flow.rb:412:40:412:48 | old_value : | +| hash_flow.rb:412:24:412:28 | hash2 [element :d] : | hash_flow.rb:412:51:412:59 | new_value : | +| hash_flow.rb:412:24:412:28 | hash2 [element :f] : | hash_flow.rb:412:12:416:7 | call to merge [element :f] : | +| hash_flow.rb:412:24:412:28 | hash2 [element :f] : | hash_flow.rb:412:40:412:48 | old_value : | +| hash_flow.rb:412:24:412:28 | hash2 [element :f] : | hash_flow.rb:412:51:412:59 | new_value : | +| hash_flow.rb:412:40:412:48 | old_value : | hash_flow.rb:414:14:414:22 | old_value | +| hash_flow.rb:412:51:412:59 | new_value : | hash_flow.rb:415:14:415:22 | new_value | +| hash_flow.rb:417:11:417:14 | hash [element :a] : | hash_flow.rb:417:11:417:18 | ...[...] : | +| hash_flow.rb:417:11:417:18 | ...[...] : | hash_flow.rb:417:10:417:19 | ( ... ) | +| hash_flow.rb:419:11:419:14 | hash [element :c] : | hash_flow.rb:419:11:419:18 | ...[...] : | +| hash_flow.rb:419:11:419:18 | ...[...] : | hash_flow.rb:419:10:419:19 | ( ... ) | +| hash_flow.rb:420:11:420:14 | hash [element :d] : | hash_flow.rb:420:11:420:18 | ...[...] : | +| hash_flow.rb:420:11:420:18 | ...[...] : | hash_flow.rb:420:10:420:19 | ( ... ) | +| hash_flow.rb:422:11:422:14 | hash [element :f] : | hash_flow.rb:422:11:422:18 | ...[...] : | +| hash_flow.rb:422:11:422:18 | ...[...] : | hash_flow.rb:422:10:422:19 | ( ... ) | +| hash_flow.rb:429:15:429:25 | call to taint : | hash_flow.rb:438:12:438:16 | hash1 [element :a] : | +| hash_flow.rb:431:15:431:25 | call to taint : | hash_flow.rb:438:12:438:16 | hash1 [element :c] : | +| hash_flow.rb:434:15:434:25 | call to taint : | hash_flow.rb:438:25:438:29 | hash2 [element :d] : | +| hash_flow.rb:436:15:436:25 | call to taint : | hash_flow.rb:438:25:438:29 | hash2 [element :f] : | +| hash_flow.rb:438:12:438:16 | [post] hash1 [element :a] : | hash_flow.rb:450:11:450:15 | hash1 [element :a] : | +| hash_flow.rb:438:12:438:16 | [post] hash1 [element :c] : | hash_flow.rb:452:11:452:15 | hash1 [element :c] : | +| hash_flow.rb:438:12:438:16 | [post] hash1 [element :d] : | hash_flow.rb:453:11:453:15 | hash1 [element :d] : | +| hash_flow.rb:438:12:438:16 | [post] hash1 [element :f] : | hash_flow.rb:455:11:455:15 | hash1 [element :f] : | +| hash_flow.rb:438:12:438:16 | hash1 [element :a] : | hash_flow.rb:438:12:438:16 | [post] hash1 [element :a] : | +| hash_flow.rb:438:12:438:16 | hash1 [element :a] : | hash_flow.rb:438:12:442:7 | call to merge! [element :a] : | +| hash_flow.rb:438:12:438:16 | hash1 [element :a] : | hash_flow.rb:438:41:438:49 | old_value : | +| hash_flow.rb:438:12:438:16 | hash1 [element :a] : | hash_flow.rb:438:52:438:60 | new_value : | +| hash_flow.rb:438:12:438:16 | hash1 [element :c] : | hash_flow.rb:438:12:438:16 | [post] hash1 [element :c] : | +| hash_flow.rb:438:12:438:16 | hash1 [element :c] : | hash_flow.rb:438:12:442:7 | call to merge! [element :c] : | +| hash_flow.rb:438:12:438:16 | hash1 [element :c] : | hash_flow.rb:438:41:438:49 | old_value : | +| hash_flow.rb:438:12:438:16 | hash1 [element :c] : | hash_flow.rb:438:52:438:60 | new_value : | +| hash_flow.rb:438:12:442:7 | call to merge! [element :a] : | hash_flow.rb:443:11:443:14 | hash [element :a] : | +| hash_flow.rb:438:12:442:7 | call to merge! [element :c] : | hash_flow.rb:445:11:445:14 | hash [element :c] : | +| hash_flow.rb:438:12:442:7 | call to merge! [element :d] : | hash_flow.rb:446:11:446:14 | hash [element :d] : | +| hash_flow.rb:438:12:442:7 | call to merge! [element :f] : | hash_flow.rb:448:11:448:14 | hash [element :f] : | +| hash_flow.rb:438:25:438:29 | hash2 [element :d] : | hash_flow.rb:438:12:438:16 | [post] hash1 [element :d] : | +| hash_flow.rb:438:25:438:29 | hash2 [element :d] : | hash_flow.rb:438:12:442:7 | call to merge! [element :d] : | +| hash_flow.rb:438:25:438:29 | hash2 [element :d] : | hash_flow.rb:438:41:438:49 | old_value : | +| hash_flow.rb:438:25:438:29 | hash2 [element :d] : | hash_flow.rb:438:52:438:60 | new_value : | +| hash_flow.rb:438:25:438:29 | hash2 [element :f] : | hash_flow.rb:438:12:438:16 | [post] hash1 [element :f] : | +| hash_flow.rb:438:25:438:29 | hash2 [element :f] : | hash_flow.rb:438:12:442:7 | call to merge! [element :f] : | +| hash_flow.rb:438:25:438:29 | hash2 [element :f] : | hash_flow.rb:438:41:438:49 | old_value : | +| hash_flow.rb:438:25:438:29 | hash2 [element :f] : | hash_flow.rb:438:52:438:60 | new_value : | +| hash_flow.rb:438:41:438:49 | old_value : | hash_flow.rb:440:14:440:22 | old_value | +| hash_flow.rb:438:52:438:60 | new_value : | hash_flow.rb:441:14:441:22 | new_value | +| hash_flow.rb:443:11:443:14 | hash [element :a] : | hash_flow.rb:443:11:443:18 | ...[...] : | +| hash_flow.rb:443:11:443:18 | ...[...] : | hash_flow.rb:443:10:443:19 | ( ... ) | +| hash_flow.rb:445:11:445:14 | hash [element :c] : | hash_flow.rb:445:11:445:18 | ...[...] : | +| hash_flow.rb:445:11:445:18 | ...[...] : | hash_flow.rb:445:10:445:19 | ( ... ) | +| hash_flow.rb:446:11:446:14 | hash [element :d] : | hash_flow.rb:446:11:446:18 | ...[...] : | +| hash_flow.rb:446:11:446:18 | ...[...] : | hash_flow.rb:446:10:446:19 | ( ... ) | +| hash_flow.rb:448:11:448:14 | hash [element :f] : | hash_flow.rb:448:11:448:18 | ...[...] : | +| hash_flow.rb:448:11:448:18 | ...[...] : | hash_flow.rb:448:10:448:19 | ( ... ) | +| hash_flow.rb:450:11:450:15 | hash1 [element :a] : | hash_flow.rb:450:11:450:19 | ...[...] : | +| hash_flow.rb:450:11:450:19 | ...[...] : | hash_flow.rb:450:10:450:20 | ( ... ) | +| hash_flow.rb:452:11:452:15 | hash1 [element :c] : | hash_flow.rb:452:11:452:19 | ...[...] : | +| hash_flow.rb:452:11:452:19 | ...[...] : | hash_flow.rb:452:10:452:20 | ( ... ) | +| hash_flow.rb:453:11:453:15 | hash1 [element :d] : | hash_flow.rb:453:11:453:19 | ...[...] : | +| hash_flow.rb:453:11:453:19 | ...[...] : | hash_flow.rb:453:10:453:20 | ( ... ) | +| hash_flow.rb:455:11:455:15 | hash1 [element :f] : | hash_flow.rb:455:11:455:19 | ...[...] : | +| hash_flow.rb:455:11:455:19 | ...[...] : | hash_flow.rb:455:10:455:20 | ( ... ) | +| hash_flow.rb:462:15:462:25 | call to taint : | hash_flow.rb:465:9:465:12 | hash [element :a] : | +| hash_flow.rb:465:9:465:12 | hash [element :a] : | hash_flow.rb:465:9:465:22 | call to rassoc [element 1] : | +| hash_flow.rb:465:9:465:22 | call to rassoc [element 1] : | hash_flow.rb:467:10:467:10 | b [element 1] : | +| hash_flow.rb:467:10:467:10 | b [element 1] : | hash_flow.rb:467:10:467:13 | ...[...] | +| hash_flow.rb:474:15:474:25 | call to taint : | hash_flow.rb:477:9:477:12 | hash [element :a] : | +| hash_flow.rb:477:9:477:12 | hash [element :a] : | hash_flow.rb:477:9:481:7 | call to reject [element :a] : | +| hash_flow.rb:477:9:477:12 | hash [element :a] : | hash_flow.rb:477:29:477:33 | value : | +| hash_flow.rb:477:9:481:7 | call to reject [element :a] : | hash_flow.rb:482:10:482:10 | b [element :a] : | +| hash_flow.rb:477:29:477:33 | value : | hash_flow.rb:479:14:479:18 | value | +| hash_flow.rb:482:10:482:10 | b [element :a] : | hash_flow.rb:482:10:482:14 | ...[...] | +| hash_flow.rb:489:15:489:25 | call to taint : | hash_flow.rb:492:9:492:12 | hash [element :a] : | +| hash_flow.rb:489:15:489:25 | call to taint : | hash_flow.rb:498:10:498:13 | hash [element :a] : | +| hash_flow.rb:492:9:492:12 | [post] hash [element :a] : | hash_flow.rb:498:10:498:13 | hash [element :a] : | +| hash_flow.rb:492:9:492:12 | hash [element :a] : | hash_flow.rb:492:9:492:12 | [post] hash [element :a] : | +| hash_flow.rb:492:9:492:12 | hash [element :a] : | hash_flow.rb:492:9:496:7 | call to reject! [element :a] : | +| hash_flow.rb:492:9:492:12 | hash [element :a] : | hash_flow.rb:492:30:492:34 | value : | +| hash_flow.rb:492:9:496:7 | call to reject! [element :a] : | hash_flow.rb:497:10:497:10 | b [element :a] : | +| hash_flow.rb:492:30:492:34 | value : | hash_flow.rb:494:14:494:18 | value | +| hash_flow.rb:497:10:497:10 | b [element :a] : | hash_flow.rb:497:10:497:14 | ...[...] | +| hash_flow.rb:498:10:498:13 | hash [element :a] : | hash_flow.rb:498:10:498:17 | ...[...] | +| hash_flow.rb:505:15:505:25 | call to taint : | hash_flow.rb:512:19:512:22 | hash [element :a] : | +| hash_flow.rb:507:15:507:25 | call to taint : | hash_flow.rb:512:19:512:22 | hash [element :c] : | +| hash_flow.rb:512:5:512:9 | [post] hash2 [element :a] : | hash_flow.rb:513:11:513:15 | hash2 [element :a] : | +| hash_flow.rb:512:5:512:9 | [post] hash2 [element :c] : | hash_flow.rb:515:11:515:15 | hash2 [element :c] : | +| hash_flow.rb:512:19:512:22 | hash [element :a] : | hash_flow.rb:512:5:512:9 | [post] hash2 [element :a] : | +| hash_flow.rb:512:19:512:22 | hash [element :c] : | hash_flow.rb:512:5:512:9 | [post] hash2 [element :c] : | +| hash_flow.rb:513:11:513:15 | hash2 [element :a] : | hash_flow.rb:513:11:513:19 | ...[...] : | +| hash_flow.rb:513:11:513:19 | ...[...] : | hash_flow.rb:513:10:513:20 | ( ... ) | +| hash_flow.rb:515:11:515:15 | hash2 [element :c] : | hash_flow.rb:515:11:515:19 | ...[...] : | +| hash_flow.rb:515:11:515:19 | ...[...] : | hash_flow.rb:515:10:515:20 | ( ... ) | +| hash_flow.rb:520:15:520:25 | call to taint : | hash_flow.rb:524:9:524:12 | hash [element :a] : | +| hash_flow.rb:522:15:522:25 | call to taint : | hash_flow.rb:524:9:524:12 | hash [element :c] : | +| hash_flow.rb:524:9:524:12 | hash [element :a] : | hash_flow.rb:524:9:528:7 | call to select [element :a] : | +| hash_flow.rb:524:9:524:12 | hash [element :a] : | hash_flow.rb:524:30:524:34 | value : | +| hash_flow.rb:524:9:524:12 | hash [element :c] : | hash_flow.rb:524:30:524:34 | value : | +| hash_flow.rb:524:9:528:7 | call to select [element :a] : | hash_flow.rb:529:11:529:11 | b [element :a] : | +| hash_flow.rb:524:30:524:34 | value : | hash_flow.rb:526:14:526:18 | value | +| hash_flow.rb:529:11:529:11 | b [element :a] : | hash_flow.rb:529:11:529:15 | ...[...] : | +| hash_flow.rb:529:11:529:15 | ...[...] : | hash_flow.rb:529:10:529:16 | ( ... ) | +| hash_flow.rb:536:15:536:25 | call to taint : | hash_flow.rb:540:5:540:8 | hash [element :a] : | +| hash_flow.rb:538:15:538:25 | call to taint : | hash_flow.rb:540:5:540:8 | hash [element :c] : | +| hash_flow.rb:540:5:540:8 | [post] hash [element :a] : | hash_flow.rb:545:11:545:14 | hash [element :a] : | +| hash_flow.rb:540:5:540:8 | hash [element :a] : | hash_flow.rb:540:5:540:8 | [post] hash [element :a] : | +| hash_flow.rb:540:5:540:8 | hash [element :a] : | hash_flow.rb:540:27:540:31 | value : | +| hash_flow.rb:540:5:540:8 | hash [element :c] : | hash_flow.rb:540:27:540:31 | value : | +| hash_flow.rb:540:27:540:31 | value : | hash_flow.rb:542:14:542:18 | value | +| hash_flow.rb:545:11:545:14 | hash [element :a] : | hash_flow.rb:545:11:545:18 | ...[...] : | +| hash_flow.rb:545:11:545:18 | ...[...] : | hash_flow.rb:545:10:545:19 | ( ... ) | +| hash_flow.rb:552:15:552:25 | call to taint : | hash_flow.rb:556:9:556:12 | hash [element :a] : | +| hash_flow.rb:554:15:554:25 | call to taint : | hash_flow.rb:556:9:556:12 | hash [element :c] : | +| hash_flow.rb:556:9:556:12 | [post] hash [element :a] : | hash_flow.rb:557:11:557:14 | hash [element :a] : | +| hash_flow.rb:556:9:556:12 | hash [element :a] : | hash_flow.rb:556:9:556:12 | [post] hash [element :a] : | +| hash_flow.rb:556:9:556:12 | hash [element :a] : | hash_flow.rb:556:9:556:18 | call to shift [element 1] : | +| hash_flow.rb:556:9:556:12 | hash [element :c] : | hash_flow.rb:556:9:556:18 | call to shift [element 1] : | +| hash_flow.rb:556:9:556:18 | call to shift [element 1] : | hash_flow.rb:559:11:559:11 | b [element 1] : | +| hash_flow.rb:557:11:557:14 | hash [element :a] : | hash_flow.rb:557:11:557:18 | ...[...] : | +| hash_flow.rb:557:11:557:18 | ...[...] : | hash_flow.rb:557:10:557:19 | ( ... ) | +| hash_flow.rb:559:11:559:11 | b [element 1] : | hash_flow.rb:559:11:559:14 | ...[...] : | +| hash_flow.rb:559:11:559:14 | ...[...] : | hash_flow.rb:559:10:559:15 | ( ... ) | +| hash_flow.rb:566:15:566:25 | call to taint : | hash_flow.rb:570:9:570:12 | hash [element :a] : | +| hash_flow.rb:566:15:566:25 | call to taint : | hash_flow.rb:575:9:575:12 | hash [element :a] : | +| hash_flow.rb:568:15:568:25 | call to taint : | hash_flow.rb:575:9:575:12 | hash [element :c] : | +| hash_flow.rb:570:9:570:12 | hash [element :a] : | hash_flow.rb:570:9:570:26 | call to slice [element :a] : | +| hash_flow.rb:570:9:570:26 | call to slice [element :a] : | hash_flow.rb:571:11:571:11 | b [element :a] : | +| hash_flow.rb:571:11:571:11 | b [element :a] : | hash_flow.rb:571:11:571:15 | ...[...] : | +| hash_flow.rb:571:11:571:15 | ...[...] : | hash_flow.rb:571:10:571:16 | ( ... ) | +| hash_flow.rb:575:9:575:12 | hash [element :a] : | hash_flow.rb:575:9:575:25 | call to slice [element :a] : | +| hash_flow.rb:575:9:575:12 | hash [element :c] : | hash_flow.rb:575:9:575:25 | call to slice [element :c] : | +| hash_flow.rb:575:9:575:25 | call to slice [element :a] : | hash_flow.rb:576:11:576:11 | c [element :a] : | +| hash_flow.rb:575:9:575:25 | call to slice [element :c] : | hash_flow.rb:578:11:578:11 | c [element :c] : | +| hash_flow.rb:576:11:576:11 | c [element :a] : | hash_flow.rb:576:11:576:15 | ...[...] : | +| hash_flow.rb:576:11:576:15 | ...[...] : | hash_flow.rb:576:10:576:16 | ( ... ) | +| hash_flow.rb:578:11:578:11 | c [element :c] : | hash_flow.rb:578:11:578:15 | ...[...] : | +| hash_flow.rb:578:11:578:15 | ...[...] : | hash_flow.rb:578:10:578:16 | ( ... ) | +| hash_flow.rb:585:15:585:25 | call to taint : | hash_flow.rb:589:9:589:12 | hash [element :a] : | +| hash_flow.rb:587:15:587:25 | call to taint : | hash_flow.rb:589:9:589:12 | hash [element :c] : | +| hash_flow.rb:589:9:589:12 | hash [element :a] : | hash_flow.rb:589:9:589:17 | call to to_a [element, element 1] : | +| hash_flow.rb:589:9:589:12 | hash [element :c] : | hash_flow.rb:589:9:589:17 | call to to_a [element, element 1] : | +| hash_flow.rb:589:9:589:17 | call to to_a [element, element 1] : | hash_flow.rb:591:11:591:11 | a [element, element 1] : | +| hash_flow.rb:591:11:591:11 | a [element, element 1] : | hash_flow.rb:591:11:591:14 | ...[...] [element 1] : | +| hash_flow.rb:591:11:591:14 | ...[...] [element 1] : | hash_flow.rb:591:11:591:17 | ...[...] : | +| hash_flow.rb:591:11:591:17 | ...[...] : | hash_flow.rb:591:10:591:18 | ( ... ) | +| hash_flow.rb:598:15:598:25 | call to taint : | hash_flow.rb:602:9:602:12 | hash [element :a] : | +| hash_flow.rb:598:15:598:25 | call to taint : | hash_flow.rb:607:9:607:12 | hash [element :a] : | +| hash_flow.rb:600:15:600:25 | call to taint : | hash_flow.rb:602:9:602:12 | hash [element :c] : | +| hash_flow.rb:600:15:600:25 | call to taint : | hash_flow.rb:607:9:607:12 | hash [element :c] : | +| hash_flow.rb:602:9:602:12 | hash [element :a] : | hash_flow.rb:602:9:602:17 | call to to_h [element :a] : | +| hash_flow.rb:602:9:602:12 | hash [element :c] : | hash_flow.rb:602:9:602:17 | call to to_h [element :c] : | +| hash_flow.rb:602:9:602:17 | call to to_h [element :a] : | hash_flow.rb:603:11:603:11 | a [element :a] : | +| hash_flow.rb:602:9:602:17 | call to to_h [element :c] : | hash_flow.rb:605:11:605:11 | a [element :c] : | +| hash_flow.rb:603:11:603:11 | a [element :a] : | hash_flow.rb:603:11:603:15 | ...[...] : | +| hash_flow.rb:603:11:603:15 | ...[...] : | hash_flow.rb:603:10:603:16 | ( ... ) | +| hash_flow.rb:605:11:605:11 | a [element :c] : | hash_flow.rb:605:11:605:15 | ...[...] : | +| hash_flow.rb:605:11:605:15 | ...[...] : | hash_flow.rb:605:10:605:16 | ( ... ) | +| hash_flow.rb:607:9:607:12 | hash [element :a] : | hash_flow.rb:607:28:607:32 | value : | +| hash_flow.rb:607:9:607:12 | hash [element :c] : | hash_flow.rb:607:28:607:32 | value : | +| hash_flow.rb:607:9:611:7 | call to to_h [element] : | hash_flow.rb:612:11:612:11 | b [element] : | +| hash_flow.rb:607:28:607:32 | value : | hash_flow.rb:609:14:609:18 | value | +| hash_flow.rb:610:14:610:24 | call to taint : | hash_flow.rb:607:9:611:7 | call to to_h [element] : | +| hash_flow.rb:612:11:612:11 | b [element] : | hash_flow.rb:612:11:612:15 | ...[...] : | +| hash_flow.rb:612:11:612:15 | ...[...] : | hash_flow.rb:612:10:612:16 | ( ... ) | +| hash_flow.rb:619:15:619:25 | call to taint : | hash_flow.rb:623:9:623:12 | hash [element :a] : | +| hash_flow.rb:621:15:621:25 | call to taint : | hash_flow.rb:623:9:623:12 | hash [element :c] : | +| hash_flow.rb:623:9:623:12 | hash [element :a] : | hash_flow.rb:623:9:623:45 | call to transform_keys [element] : | +| hash_flow.rb:623:9:623:12 | hash [element :c] : | hash_flow.rb:623:9:623:45 | call to transform_keys [element] : | +| hash_flow.rb:623:9:623:45 | call to transform_keys [element] : | hash_flow.rb:624:11:624:11 | a [element] : | +| hash_flow.rb:623:9:623:45 | call to transform_keys [element] : | hash_flow.rb:625:11:625:11 | a [element] : | +| hash_flow.rb:623:9:623:45 | call to transform_keys [element] : | hash_flow.rb:626:11:626:11 | a [element] : | +| hash_flow.rb:624:11:624:11 | a [element] : | hash_flow.rb:624:11:624:16 | ...[...] : | +| hash_flow.rb:624:11:624:16 | ...[...] : | hash_flow.rb:624:10:624:17 | ( ... ) | +| hash_flow.rb:625:11:625:11 | a [element] : | hash_flow.rb:625:11:625:16 | ...[...] : | +| hash_flow.rb:625:11:625:16 | ...[...] : | hash_flow.rb:625:10:625:17 | ( ... ) | +| hash_flow.rb:626:11:626:11 | a [element] : | hash_flow.rb:626:11:626:16 | ...[...] : | +| hash_flow.rb:626:11:626:16 | ...[...] : | hash_flow.rb:626:10:626:17 | ( ... ) | +| hash_flow.rb:633:15:633:25 | call to taint : | hash_flow.rb:637:5:637:8 | hash [element :a] : | +| hash_flow.rb:635:15:635:25 | call to taint : | hash_flow.rb:637:5:637:8 | hash [element :c] : | +| hash_flow.rb:637:5:637:8 | [post] hash [element] : | hash_flow.rb:638:11:638:14 | hash [element] : | +| hash_flow.rb:637:5:637:8 | [post] hash [element] : | hash_flow.rb:639:11:639:14 | hash [element] : | +| hash_flow.rb:637:5:637:8 | [post] hash [element] : | hash_flow.rb:640:11:640:14 | hash [element] : | +| hash_flow.rb:637:5:637:8 | hash [element :a] : | hash_flow.rb:637:5:637:8 | [post] hash [element] : | +| hash_flow.rb:637:5:637:8 | hash [element :c] : | hash_flow.rb:637:5:637:8 | [post] hash [element] : | +| hash_flow.rb:638:11:638:14 | hash [element] : | hash_flow.rb:638:11:638:19 | ...[...] : | +| hash_flow.rb:638:11:638:19 | ...[...] : | hash_flow.rb:638:10:638:20 | ( ... ) | +| hash_flow.rb:639:11:639:14 | hash [element] : | hash_flow.rb:639:11:639:19 | ...[...] : | +| hash_flow.rb:639:11:639:19 | ...[...] : | hash_flow.rb:639:10:639:20 | ( ... ) | +| hash_flow.rb:640:11:640:14 | hash [element] : | hash_flow.rb:640:11:640:19 | ...[...] : | +| hash_flow.rb:640:11:640:19 | ...[...] : | hash_flow.rb:640:10:640:20 | ( ... ) | +| hash_flow.rb:647:15:647:25 | call to taint : | hash_flow.rb:651:9:651:12 | hash [element :a] : | +| hash_flow.rb:647:15:647:25 | call to taint : | hash_flow.rb:655:11:655:14 | hash [element :a] : | +| hash_flow.rb:649:15:649:25 | call to taint : | hash_flow.rb:651:9:651:12 | hash [element :c] : | +| hash_flow.rb:651:9:651:12 | hash [element :a] : | hash_flow.rb:651:35:651:39 | value : | +| hash_flow.rb:651:9:651:12 | hash [element :c] : | hash_flow.rb:651:35:651:39 | value : | +| hash_flow.rb:651:9:654:7 | call to transform_values [element] : | hash_flow.rb:656:11:656:11 | b [element] : | +| hash_flow.rb:651:35:651:39 | value : | hash_flow.rb:652:14:652:18 | value | +| hash_flow.rb:653:9:653:19 | call to taint : | hash_flow.rb:651:9:654:7 | call to transform_values [element] : | +| hash_flow.rb:655:11:655:14 | hash [element :a] : | hash_flow.rb:655:11:655:18 | ...[...] : | +| hash_flow.rb:655:11:655:18 | ...[...] : | hash_flow.rb:655:10:655:19 | ( ... ) | +| hash_flow.rb:656:11:656:11 | b [element] : | hash_flow.rb:656:11:656:15 | ...[...] : | +| hash_flow.rb:656:11:656:15 | ...[...] : | hash_flow.rb:656:10:656:16 | ( ... ) | +| hash_flow.rb:663:15:663:25 | call to taint : | hash_flow.rb:667:5:667:8 | hash [element :a] : | +| hash_flow.rb:665:15:665:25 | call to taint : | hash_flow.rb:667:5:667:8 | hash [element :c] : | +| hash_flow.rb:667:5:667:8 | [post] hash [element] : | hash_flow.rb:671:11:671:14 | hash [element] : | +| hash_flow.rb:667:5:667:8 | hash [element :a] : | hash_flow.rb:667:32:667:36 | value : | +| hash_flow.rb:667:5:667:8 | hash [element :c] : | hash_flow.rb:667:32:667:36 | value : | +| hash_flow.rb:667:32:667:36 | value : | hash_flow.rb:668:14:668:18 | value | +| hash_flow.rb:669:9:669:19 | call to taint : | hash_flow.rb:667:5:667:8 | [post] hash [element] : | +| hash_flow.rb:671:11:671:14 | hash [element] : | hash_flow.rb:671:11:671:18 | ...[...] : | +| hash_flow.rb:671:11:671:18 | ...[...] : | hash_flow.rb:671:10:671:19 | ( ... ) | +| hash_flow.rb:678:15:678:25 | call to taint : | hash_flow.rb:687:12:687:16 | hash1 [element :a] : | +| hash_flow.rb:680:15:680:25 | call to taint : | hash_flow.rb:687:12:687:16 | hash1 [element :c] : | +| hash_flow.rb:683:15:683:25 | call to taint : | hash_flow.rb:687:25:687:29 | hash2 [element :d] : | +| hash_flow.rb:685:15:685:25 | call to taint : | hash_flow.rb:687:25:687:29 | hash2 [element :f] : | +| hash_flow.rb:687:12:687:16 | [post] hash1 [element :a] : | hash_flow.rb:699:11:699:15 | hash1 [element :a] : | +| hash_flow.rb:687:12:687:16 | [post] hash1 [element :c] : | hash_flow.rb:701:11:701:15 | hash1 [element :c] : | +| hash_flow.rb:687:12:687:16 | [post] hash1 [element :d] : | hash_flow.rb:702:11:702:15 | hash1 [element :d] : | +| hash_flow.rb:687:12:687:16 | [post] hash1 [element :f] : | hash_flow.rb:704:11:704:15 | hash1 [element :f] : | +| hash_flow.rb:687:12:687:16 | hash1 [element :a] : | hash_flow.rb:687:12:687:16 | [post] hash1 [element :a] : | +| hash_flow.rb:687:12:687:16 | hash1 [element :a] : | hash_flow.rb:687:12:691:7 | call to update [element :a] : | +| hash_flow.rb:687:12:687:16 | hash1 [element :a] : | hash_flow.rb:687:41:687:49 | old_value : | +| hash_flow.rb:687:12:687:16 | hash1 [element :a] : | hash_flow.rb:687:52:687:60 | new_value : | +| hash_flow.rb:687:12:687:16 | hash1 [element :c] : | hash_flow.rb:687:12:687:16 | [post] hash1 [element :c] : | +| hash_flow.rb:687:12:687:16 | hash1 [element :c] : | hash_flow.rb:687:12:691:7 | call to update [element :c] : | +| hash_flow.rb:687:12:687:16 | hash1 [element :c] : | hash_flow.rb:687:41:687:49 | old_value : | +| hash_flow.rb:687:12:687:16 | hash1 [element :c] : | hash_flow.rb:687:52:687:60 | new_value : | +| hash_flow.rb:687:12:691:7 | call to update [element :a] : | hash_flow.rb:692:11:692:14 | hash [element :a] : | +| hash_flow.rb:687:12:691:7 | call to update [element :c] : | hash_flow.rb:694:11:694:14 | hash [element :c] : | +| hash_flow.rb:687:12:691:7 | call to update [element :d] : | hash_flow.rb:695:11:695:14 | hash [element :d] : | +| hash_flow.rb:687:12:691:7 | call to update [element :f] : | hash_flow.rb:697:11:697:14 | hash [element :f] : | +| hash_flow.rb:687:25:687:29 | hash2 [element :d] : | hash_flow.rb:687:12:687:16 | [post] hash1 [element :d] : | +| hash_flow.rb:687:25:687:29 | hash2 [element :d] : | hash_flow.rb:687:12:691:7 | call to update [element :d] : | +| hash_flow.rb:687:25:687:29 | hash2 [element :d] : | hash_flow.rb:687:41:687:49 | old_value : | +| hash_flow.rb:687:25:687:29 | hash2 [element :d] : | hash_flow.rb:687:52:687:60 | new_value : | +| hash_flow.rb:687:25:687:29 | hash2 [element :f] : | hash_flow.rb:687:12:687:16 | [post] hash1 [element :f] : | +| hash_flow.rb:687:25:687:29 | hash2 [element :f] : | hash_flow.rb:687:12:691:7 | call to update [element :f] : | +| hash_flow.rb:687:25:687:29 | hash2 [element :f] : | hash_flow.rb:687:41:687:49 | old_value : | +| hash_flow.rb:687:25:687:29 | hash2 [element :f] : | hash_flow.rb:687:52:687:60 | new_value : | +| hash_flow.rb:687:41:687:49 | old_value : | hash_flow.rb:689:14:689:22 | old_value | +| hash_flow.rb:687:52:687:60 | new_value : | hash_flow.rb:690:14:690:22 | new_value | +| hash_flow.rb:692:11:692:14 | hash [element :a] : | hash_flow.rb:692:11:692:18 | ...[...] : | +| hash_flow.rb:692:11:692:18 | ...[...] : | hash_flow.rb:692:10:692:19 | ( ... ) | +| hash_flow.rb:694:11:694:14 | hash [element :c] : | hash_flow.rb:694:11:694:18 | ...[...] : | +| hash_flow.rb:694:11:694:18 | ...[...] : | hash_flow.rb:694:10:694:19 | ( ... ) | +| hash_flow.rb:695:11:695:14 | hash [element :d] : | hash_flow.rb:695:11:695:18 | ...[...] : | +| hash_flow.rb:695:11:695:18 | ...[...] : | hash_flow.rb:695:10:695:19 | ( ... ) | +| hash_flow.rb:697:11:697:14 | hash [element :f] : | hash_flow.rb:697:11:697:18 | ...[...] : | +| hash_flow.rb:697:11:697:18 | ...[...] : | hash_flow.rb:697:10:697:19 | ( ... ) | +| hash_flow.rb:699:11:699:15 | hash1 [element :a] : | hash_flow.rb:699:11:699:19 | ...[...] : | +| hash_flow.rb:699:11:699:19 | ...[...] : | hash_flow.rb:699:10:699:20 | ( ... ) | +| hash_flow.rb:701:11:701:15 | hash1 [element :c] : | hash_flow.rb:701:11:701:19 | ...[...] : | +| hash_flow.rb:701:11:701:19 | ...[...] : | hash_flow.rb:701:10:701:20 | ( ... ) | +| hash_flow.rb:702:11:702:15 | hash1 [element :d] : | hash_flow.rb:702:11:702:19 | ...[...] : | +| hash_flow.rb:702:11:702:19 | ...[...] : | hash_flow.rb:702:10:702:20 | ( ... ) | +| hash_flow.rb:704:11:704:15 | hash1 [element :f] : | hash_flow.rb:704:11:704:19 | ...[...] : | +| hash_flow.rb:704:11:704:19 | ...[...] : | hash_flow.rb:704:10:704:20 | ( ... ) | +| hash_flow.rb:711:15:711:25 | call to taint : | hash_flow.rb:715:9:715:12 | hash [element :a] : | +| hash_flow.rb:713:15:713:25 | call to taint : | hash_flow.rb:715:9:715:12 | hash [element :c] : | +| hash_flow.rb:715:9:715:12 | hash [element :a] : | hash_flow.rb:715:9:715:19 | call to values [element] : | +| hash_flow.rb:715:9:715:12 | hash [element :c] : | hash_flow.rb:715:9:715:19 | call to values [element] : | +| hash_flow.rb:715:9:715:19 | call to values [element] : | hash_flow.rb:716:11:716:11 | a [element] : | +| hash_flow.rb:716:11:716:11 | a [element] : | hash_flow.rb:716:11:716:14 | ...[...] : | +| hash_flow.rb:716:11:716:14 | ...[...] : | hash_flow.rb:716:10:716:15 | ( ... ) | +| hash_flow.rb:723:15:723:25 | call to taint : | hash_flow.rb:727:9:727:12 | hash [element :a] : | +| hash_flow.rb:723:15:723:25 | call to taint : | hash_flow.rb:729:9:729:12 | hash [element :a] : | +| hash_flow.rb:725:15:725:25 | call to taint : | hash_flow.rb:729:9:729:12 | hash [element :c] : | +| hash_flow.rb:727:9:727:12 | hash [element :a] : | hash_flow.rb:727:9:727:26 | call to values_at [element 0] : | +| hash_flow.rb:727:9:727:26 | call to values_at [element 0] : | hash_flow.rb:728:10:728:10 | b [element 0] : | +| hash_flow.rb:728:10:728:10 | b [element 0] : | hash_flow.rb:728:10:728:13 | ...[...] | +| hash_flow.rb:729:9:729:12 | hash [element :a] : | hash_flow.rb:729:9:729:31 | call to fetch_values [element] : | +| hash_flow.rb:729:9:729:12 | hash [element :c] : | hash_flow.rb:729:9:729:31 | call to fetch_values [element] : | +| hash_flow.rb:729:9:729:31 | call to fetch_values [element] : | hash_flow.rb:730:10:730:10 | b [element] : | +| hash_flow.rb:730:10:730:10 | b [element] : | hash_flow.rb:730:10:730:13 | ...[...] | +| hash_flow.rb:737:15:737:25 | call to taint : | hash_flow.rb:746:16:746:20 | hash1 [element :a] : | +| hash_flow.rb:739:15:739:25 | call to taint : | hash_flow.rb:746:16:746:20 | hash1 [element :c] : | +| hash_flow.rb:742:15:742:25 | call to taint : | hash_flow.rb:746:44:746:48 | hash2 [element :d] : | +| hash_flow.rb:744:15:744:25 | call to taint : | hash_flow.rb:746:44:746:48 | hash2 [element :f] : | +| hash_flow.rb:746:14:746:20 | ** ... [element :a] : | hash_flow.rb:747:10:747:13 | hash [element :a] : | +| hash_flow.rb:746:14:746:20 | ** ... [element :c] : | hash_flow.rb:749:10:749:13 | hash [element :c] : | +| hash_flow.rb:746:16:746:20 | hash1 [element :a] : | hash_flow.rb:746:14:746:20 | ** ... [element :a] : | +| hash_flow.rb:746:16:746:20 | hash1 [element :c] : | hash_flow.rb:746:14:746:20 | ** ... [element :c] : | +| hash_flow.rb:746:29:746:39 | call to taint : | hash_flow.rb:753:10:753:13 | hash [element :g] : | +| hash_flow.rb:746:42:746:48 | ** ... [element :d] : | hash_flow.rb:750:10:750:13 | hash [element :d] : | +| hash_flow.rb:746:42:746:48 | ** ... [element :f] : | hash_flow.rb:752:10:752:13 | hash [element :f] : | +| hash_flow.rb:746:44:746:48 | hash2 [element :d] : | hash_flow.rb:746:42:746:48 | ** ... [element :d] : | +| hash_flow.rb:746:44:746:48 | hash2 [element :f] : | hash_flow.rb:746:42:746:48 | ** ... [element :f] : | +| hash_flow.rb:747:10:747:13 | hash [element :a] : | hash_flow.rb:747:10:747:17 | ...[...] | +| hash_flow.rb:749:10:749:13 | hash [element :c] : | hash_flow.rb:749:10:749:17 | ...[...] | +| hash_flow.rb:750:10:750:13 | hash [element :d] : | hash_flow.rb:750:10:750:17 | ...[...] | +| hash_flow.rb:752:10:752:13 | hash [element :f] : | hash_flow.rb:752:10:752:17 | ...[...] | +| hash_flow.rb:753:10:753:13 | hash [element :g] : | hash_flow.rb:753:10:753:17 | ...[...] | nodes | hash_flow.rb:11:15:11:24 | call to taint : | semmle.label | call to taint : | | hash_flow.rb:13:12:13:21 | call to taint : | semmle.label | call to taint : | @@ -590,515 +598,525 @@ nodes | hash_flow.rb:68:22:68:31 | call to taint : | semmle.label | call to taint : | | hash_flow.rb:69:10:69:14 | hash4 [element :a] : | semmle.label | hash4 [element :a] : | | hash_flow.rb:69:10:69:18 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:76:13:76:42 | call to [] [element :a] : | semmle.label | call to [] [element :a] : | +| hash_flow.rb:72:13:72:45 | ...[...] [element a] : | semmle.label | ...[...] [element a] : | +| hash_flow.rb:72:18:72:34 | Pair [pair a] : | semmle.label | Pair [pair a] : | +| hash_flow.rb:72:25:72:34 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:73:10:73:14 | hash5 [element a] : | semmle.label | hash5 [element a] : | +| hash_flow.rb:73:10:73:19 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:76:13:76:47 | ...[...] [element a] : | semmle.label | ...[...] [element a] : | +| hash_flow.rb:76:19:76:35 | Pair [pair a] : | semmle.label | Pair [pair a] : | | hash_flow.rb:76:26:76:35 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:77:10:77:14 | hash1 [element :a] : | semmle.label | hash1 [element :a] : | -| hash_flow.rb:77:10:77:18 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:85:15:85:24 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:88:13:88:34 | call to try_convert [element :a] : | semmle.label | call to try_convert [element :a] : | -| hash_flow.rb:88:30:88:33 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:89:10:89:14 | hash2 [element :a] : | semmle.label | hash2 [element :a] : | -| hash_flow.rb:89:10:89:18 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:97:21:97:30 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:98:10:98:10 | b | semmle.label | b | -| hash_flow.rb:105:9:105:12 | [post] hash [element :a] : | semmle.label | [post] hash [element :a] : | -| hash_flow.rb:105:9:105:34 | call to store : | semmle.label | call to store : | -| hash_flow.rb:105:24:105:33 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:106:10:106:13 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:106:10:106:17 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:107:10:107:10 | b | semmle.label | b | -| hash_flow.rb:110:9:110:12 | [post] hash [element] : | semmle.label | [post] hash [element] : | -| hash_flow.rb:110:9:110:33 | call to store : | semmle.label | call to store : | -| hash_flow.rb:110:23:110:32 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:111:10:111:13 | hash [element] : | semmle.label | hash [element] : | -| hash_flow.rb:111:10:111:17 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:112:10:112:13 | hash [element] : | semmle.label | hash [element] : | -| hash_flow.rb:112:10:112:17 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:113:10:113:10 | c | semmle.label | c | -| hash_flow.rb:120:15:120:24 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:123:5:123:8 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:123:18:123:29 | key_or_value : | semmle.label | key_or_value : | -| hash_flow.rb:124:14:124:25 | key_or_value | semmle.label | key_or_value | -| hash_flow.rb:126:5:126:8 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:126:22:126:26 | value : | semmle.label | value : | -| hash_flow.rb:128:14:128:18 | value | semmle.label | value | -| hash_flow.rb:136:15:136:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:139:9:139:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:139:9:139:22 | call to assoc [element 1] : | semmle.label | call to assoc [element 1] : | -| hash_flow.rb:141:10:141:10 | b [element 1] : | semmle.label | b [element 1] : | -| hash_flow.rb:141:10:141:13 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:142:10:142:10 | b [element 1] : | semmle.label | b [element 1] : | -| hash_flow.rb:142:10:142:13 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:143:9:143:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:143:9:143:21 | call to assoc [element 1] : | semmle.label | call to assoc [element 1] : | -| hash_flow.rb:144:10:144:10 | c [element 1] : | semmle.label | c [element 1] : | -| hash_flow.rb:144:10:144:13 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:162:15:162:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:165:9:165:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:165:9:165:20 | call to compact [element :a] : | semmle.label | call to compact [element :a] : | -| hash_flow.rb:166:10:166:10 | a [element :a] : | semmle.label | a [element :a] : | -| hash_flow.rb:166:10:166:14 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:174:15:174:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:177:9:177:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:177:9:177:23 | call to delete : | semmle.label | call to delete : | -| hash_flow.rb:178:10:178:10 | a | semmle.label | a | -| hash_flow.rb:186:15:186:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:189:9:189:12 | [post] hash [element :a] : | semmle.label | [post] hash [element :a] : | -| hash_flow.rb:189:9:189:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:189:9:192:7 | call to delete_if [element :a] : | semmle.label | call to delete_if [element :a] : | -| hash_flow.rb:189:33:189:37 | value : | semmle.label | value : | -| hash_flow.rb:191:14:191:18 | value | semmle.label | value | -| hash_flow.rb:193:10:193:10 | a [element :a] : | semmle.label | a [element :a] : | -| hash_flow.rb:193:10:193:14 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:194:10:194:13 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:194:10:194:17 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:202:15:202:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:205:19:205:29 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:209:10:209:13 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:209:10:209:21 | call to dig | semmle.label | call to dig | -| hash_flow.rb:211:10:211:13 | hash [element :c, element :d] : | semmle.label | hash [element :c, element :d] : | -| hash_flow.rb:211:10:211:24 | call to dig | semmle.label | call to dig | -| hash_flow.rb:219:15:219:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:222:9:222:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:222:9:225:7 | call to each [element :a] : | semmle.label | call to each [element :a] : | -| hash_flow.rb:222:28:222:32 | value : | semmle.label | value : | -| hash_flow.rb:224:14:224:18 | value | semmle.label | value | -| hash_flow.rb:226:10:226:10 | x [element :a] : | semmle.label | x [element :a] : | -| hash_flow.rb:226:10:226:14 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:234:15:234:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:237:9:237:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:237:9:239:7 | call to each_key [element :a] : | semmle.label | call to each_key [element :a] : | -| hash_flow.rb:240:10:240:10 | x [element :a] : | semmle.label | x [element :a] : | -| hash_flow.rb:240:10:240:14 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:248:15:248:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:251:9:251:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:251:9:254:7 | call to each_pair [element :a] : | semmle.label | call to each_pair [element :a] : | -| hash_flow.rb:251:33:251:37 | value : | semmle.label | value : | -| hash_flow.rb:253:14:253:18 | value | semmle.label | value | -| hash_flow.rb:255:10:255:10 | x [element :a] : | semmle.label | x [element :a] : | -| hash_flow.rb:255:10:255:14 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:263:15:263:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:266:9:266:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:266:9:268:7 | call to each_value [element :a] : | semmle.label | call to each_value [element :a] : | -| hash_flow.rb:266:29:266:33 | value : | semmle.label | value : | -| hash_flow.rb:267:14:267:18 | value | semmle.label | value | -| hash_flow.rb:269:10:269:10 | x [element :a] : | semmle.label | x [element :a] : | -| hash_flow.rb:269:10:269:14 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:279:15:279:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:282:9:282:12 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:282:9:282:28 | call to except [element :c] : | semmle.label | call to except [element :c] : | -| hash_flow.rb:285:10:285:10 | x [element :c] : | semmle.label | x [element :c] : | -| hash_flow.rb:285:10:285:14 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:293:15:293:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:295:15:295:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:297:9:297:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:297:9:297:12 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:297:9:299:7 | call to fetch : | semmle.label | call to fetch : | -| hash_flow.rb:297:20:297:30 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:297:37:297:37 | x : | semmle.label | x : | -| hash_flow.rb:298:14:298:14 | x | semmle.label | x | -| hash_flow.rb:300:10:300:10 | b | semmle.label | b | -| hash_flow.rb:301:9:301:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:301:9:301:22 | call to fetch : | semmle.label | call to fetch : | -| hash_flow.rb:302:10:302:10 | b | semmle.label | b | -| hash_flow.rb:303:9:303:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:303:9:303:35 | call to fetch : | semmle.label | call to fetch : | -| hash_flow.rb:303:24:303:34 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:304:10:304:10 | b | semmle.label | b | -| hash_flow.rb:305:9:305:35 | call to fetch : | semmle.label | call to fetch : | -| hash_flow.rb:305:24:305:34 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:306:10:306:10 | b | semmle.label | b | -| hash_flow.rb:307:9:307:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:307:9:307:12 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:307:9:307:34 | call to fetch : | semmle.label | call to fetch : | -| hash_flow.rb:307:23:307:33 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:77:10:77:14 | hash6 [element a] : | semmle.label | hash6 [element a] : | +| hash_flow.rb:77:10:77:19 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:84:13:84:42 | call to [] [element :a] : | semmle.label | call to [] [element :a] : | +| hash_flow.rb:84:26:84:35 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:85:10:85:14 | hash1 [element :a] : | semmle.label | hash1 [element :a] : | +| hash_flow.rb:85:10:85:18 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:93:15:93:24 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:96:13:96:34 | call to try_convert [element :a] : | semmle.label | call to try_convert [element :a] : | +| hash_flow.rb:96:30:96:33 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:97:10:97:14 | hash2 [element :a] : | semmle.label | hash2 [element :a] : | +| hash_flow.rb:97:10:97:18 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:105:21:105:30 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:106:10:106:10 | b | semmle.label | b | +| hash_flow.rb:113:9:113:12 | [post] hash [element :a] : | semmle.label | [post] hash [element :a] : | +| hash_flow.rb:113:9:113:34 | call to store : | semmle.label | call to store : | +| hash_flow.rb:113:24:113:33 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:114:10:114:13 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:114:10:114:17 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:115:10:115:10 | b | semmle.label | b | +| hash_flow.rb:118:9:118:12 | [post] hash [element] : | semmle.label | [post] hash [element] : | +| hash_flow.rb:118:9:118:33 | call to store : | semmle.label | call to store : | +| hash_flow.rb:118:23:118:32 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:119:10:119:13 | hash [element] : | semmle.label | hash [element] : | +| hash_flow.rb:119:10:119:17 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:120:10:120:13 | hash [element] : | semmle.label | hash [element] : | +| hash_flow.rb:120:10:120:17 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:121:10:121:10 | c | semmle.label | c | +| hash_flow.rb:128:15:128:24 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:131:5:131:8 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:131:18:131:29 | key_or_value : | semmle.label | key_or_value : | +| hash_flow.rb:132:14:132:25 | key_or_value | semmle.label | key_or_value | +| hash_flow.rb:134:5:134:8 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:134:22:134:26 | value : | semmle.label | value : | +| hash_flow.rb:136:14:136:18 | value | semmle.label | value | +| hash_flow.rb:144:15:144:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:147:9:147:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:147:9:147:22 | call to assoc [element 1] : | semmle.label | call to assoc [element 1] : | +| hash_flow.rb:149:10:149:10 | b [element 1] : | semmle.label | b [element 1] : | +| hash_flow.rb:149:10:149:13 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:150:10:150:10 | b [element 1] : | semmle.label | b [element 1] : | +| hash_flow.rb:150:10:150:13 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:151:9:151:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:151:9:151:21 | call to assoc [element 1] : | semmle.label | call to assoc [element 1] : | +| hash_flow.rb:152:10:152:10 | c [element 1] : | semmle.label | c [element 1] : | +| hash_flow.rb:152:10:152:13 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:170:15:170:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:173:9:173:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:173:9:173:20 | call to compact [element :a] : | semmle.label | call to compact [element :a] : | +| hash_flow.rb:174:10:174:10 | a [element :a] : | semmle.label | a [element :a] : | +| hash_flow.rb:174:10:174:14 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:182:15:182:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:185:9:185:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:185:9:185:23 | call to delete : | semmle.label | call to delete : | +| hash_flow.rb:186:10:186:10 | a | semmle.label | a | +| hash_flow.rb:194:15:194:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:197:9:197:12 | [post] hash [element :a] : | semmle.label | [post] hash [element :a] : | +| hash_flow.rb:197:9:197:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:197:9:200:7 | call to delete_if [element :a] : | semmle.label | call to delete_if [element :a] : | +| hash_flow.rb:197:33:197:37 | value : | semmle.label | value : | +| hash_flow.rb:199:14:199:18 | value | semmle.label | value | +| hash_flow.rb:201:10:201:10 | a [element :a] : | semmle.label | a [element :a] : | +| hash_flow.rb:201:10:201:14 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:202:10:202:13 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:202:10:202:17 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:210:15:210:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:213:19:213:29 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:217:10:217:13 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:217:10:217:21 | call to dig | semmle.label | call to dig | +| hash_flow.rb:219:10:219:13 | hash [element :c, element :d] : | semmle.label | hash [element :c, element :d] : | +| hash_flow.rb:219:10:219:24 | call to dig | semmle.label | call to dig | +| hash_flow.rb:227:15:227:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:230:9:230:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:230:9:233:7 | call to each [element :a] : | semmle.label | call to each [element :a] : | +| hash_flow.rb:230:28:230:32 | value : | semmle.label | value : | +| hash_flow.rb:232:14:232:18 | value | semmle.label | value | +| hash_flow.rb:234:10:234:10 | x [element :a] : | semmle.label | x [element :a] : | +| hash_flow.rb:234:10:234:14 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:242:15:242:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:245:9:245:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:245:9:247:7 | call to each_key [element :a] : | semmle.label | call to each_key [element :a] : | +| hash_flow.rb:248:10:248:10 | x [element :a] : | semmle.label | x [element :a] : | +| hash_flow.rb:248:10:248:14 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:256:15:256:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:259:9:259:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:259:9:262:7 | call to each_pair [element :a] : | semmle.label | call to each_pair [element :a] : | +| hash_flow.rb:259:33:259:37 | value : | semmle.label | value : | +| hash_flow.rb:261:14:261:18 | value | semmle.label | value | +| hash_flow.rb:263:10:263:10 | x [element :a] : | semmle.label | x [element :a] : | +| hash_flow.rb:263:10:263:14 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:271:15:271:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:274:9:274:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:274:9:276:7 | call to each_value [element :a] : | semmle.label | call to each_value [element :a] : | +| hash_flow.rb:274:29:274:33 | value : | semmle.label | value : | +| hash_flow.rb:275:14:275:18 | value | semmle.label | value | +| hash_flow.rb:277:10:277:10 | x [element :a] : | semmle.label | x [element :a] : | +| hash_flow.rb:277:10:277:14 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:287:15:287:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:290:9:290:12 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:290:9:290:28 | call to except [element :c] : | semmle.label | call to except [element :c] : | +| hash_flow.rb:293:10:293:10 | x [element :c] : | semmle.label | x [element :c] : | +| hash_flow.rb:293:10:293:14 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:301:15:301:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:303:15:303:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:305:9:305:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:305:9:305:12 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:305:9:307:7 | call to fetch : | semmle.label | call to fetch : | +| hash_flow.rb:305:20:305:30 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:305:37:305:37 | x : | semmle.label | x : | +| hash_flow.rb:306:14:306:14 | x | semmle.label | x | | hash_flow.rb:308:10:308:10 | b | semmle.label | b | -| hash_flow.rb:315:15:315:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:317:15:317:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:319:9:319:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:319:9:319:12 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:319:9:322:7 | call to fetch_values [element] : | semmle.label | call to fetch_values [element] : | -| hash_flow.rb:319:27:319:37 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:319:44:319:44 | x : | semmle.label | x : | -| hash_flow.rb:320:14:320:14 | x | semmle.label | x | -| hash_flow.rb:321:9:321:19 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:323:10:323:10 | b [element] : | semmle.label | b [element] : | -| hash_flow.rb:323:10:323:13 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:324:9:324:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:324:9:324:29 | call to fetch_values [element] : | semmle.label | call to fetch_values [element] : | -| hash_flow.rb:325:10:325:10 | b [element] : | semmle.label | b [element] : | -| hash_flow.rb:325:10:325:13 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:326:9:326:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:326:9:326:12 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:326:9:326:31 | call to fetch_values [element] : | semmle.label | call to fetch_values [element] : | -| hash_flow.rb:327:10:327:10 | b [element] : | semmle.label | b [element] : | -| hash_flow.rb:327:10:327:13 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:334:15:334:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:336:15:336:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:338:9:338:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:338:9:338:12 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:338:9:342:7 | call to filter [element :a] : | semmle.label | call to filter [element :a] : | -| hash_flow.rb:338:30:338:34 | value : | semmle.label | value : | -| hash_flow.rb:340:14:340:18 | value | semmle.label | value | -| hash_flow.rb:343:10:343:16 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:343:11:343:11 | b [element :a] : | semmle.label | b [element :a] : | -| hash_flow.rb:343:11:343:15 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:350:15:350:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:352:15:352:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:354:5:354:8 | [post] hash [element :a] : | semmle.label | [post] hash [element :a] : | -| hash_flow.rb:354:5:354:8 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:354:5:354:8 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:354:27:354:31 | value : | semmle.label | value : | -| hash_flow.rb:356:14:356:18 | value | semmle.label | value | -| hash_flow.rb:359:10:359:19 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:359:11:359:14 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:359:11:359:18 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:366:15:366:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:368:15:368:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:370:9:370:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:370:9:370:12 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:370:9:370:20 | call to flatten [element] : | semmle.label | call to flatten [element] : | -| hash_flow.rb:371:10:371:15 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:371:11:371:11 | b [element] : | semmle.label | b [element] : | -| hash_flow.rb:371:11:371:14 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:378:15:378:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:380:15:380:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:382:9:382:12 | [post] hash [element :a] : | semmle.label | [post] hash [element :a] : | -| hash_flow.rb:382:9:382:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:382:9:382:12 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:382:9:386:7 | call to keep_if [element :a] : | semmle.label | call to keep_if [element :a] : | -| hash_flow.rb:382:31:382:35 | value : | semmle.label | value : | -| hash_flow.rb:384:14:384:18 | value | semmle.label | value | -| hash_flow.rb:387:10:387:19 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:387:11:387:14 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:387:11:387:18 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:388:10:388:16 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:388:11:388:11 | b [element :a] : | semmle.label | b [element :a] : | -| hash_flow.rb:388:11:388:15 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:395:15:395:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:397:15:397:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:400:15:400:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:402:15:402:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:404:12:404:16 | hash1 [element :a] : | semmle.label | hash1 [element :a] : | -| hash_flow.rb:404:12:404:16 | hash1 [element :c] : | semmle.label | hash1 [element :c] : | -| hash_flow.rb:404:12:408:7 | call to merge [element :a] : | semmle.label | call to merge [element :a] : | -| hash_flow.rb:404:12:408:7 | call to merge [element :c] : | semmle.label | call to merge [element :c] : | -| hash_flow.rb:404:12:408:7 | call to merge [element :d] : | semmle.label | call to merge [element :d] : | -| hash_flow.rb:404:12:408:7 | call to merge [element :f] : | semmle.label | call to merge [element :f] : | -| hash_flow.rb:404:24:404:28 | hash2 [element :d] : | semmle.label | hash2 [element :d] : | -| hash_flow.rb:404:24:404:28 | hash2 [element :f] : | semmle.label | hash2 [element :f] : | -| hash_flow.rb:404:40:404:48 | old_value : | semmle.label | old_value : | -| hash_flow.rb:404:51:404:59 | new_value : | semmle.label | new_value : | -| hash_flow.rb:406:14:406:22 | old_value | semmle.label | old_value | -| hash_flow.rb:407:14:407:22 | new_value | semmle.label | new_value | -| hash_flow.rb:409:10:409:19 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:409:11:409:14 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:409:11:409:18 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:411:10:411:19 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:411:11:411:14 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:411:11:411:18 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:412:10:412:19 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:412:11:412:14 | hash [element :d] : | semmle.label | hash [element :d] : | -| hash_flow.rb:412:11:412:18 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:414:10:414:19 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:414:11:414:14 | hash [element :f] : | semmle.label | hash [element :f] : | -| hash_flow.rb:414:11:414:18 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:421:15:421:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:423:15:423:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:426:15:426:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:428:15:428:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:430:12:430:16 | [post] hash1 [element :a] : | semmle.label | [post] hash1 [element :a] : | -| hash_flow.rb:430:12:430:16 | [post] hash1 [element :c] : | semmle.label | [post] hash1 [element :c] : | -| hash_flow.rb:430:12:430:16 | [post] hash1 [element :d] : | semmle.label | [post] hash1 [element :d] : | -| hash_flow.rb:430:12:430:16 | [post] hash1 [element :f] : | semmle.label | [post] hash1 [element :f] : | -| hash_flow.rb:430:12:430:16 | hash1 [element :a] : | semmle.label | hash1 [element :a] : | -| hash_flow.rb:430:12:430:16 | hash1 [element :c] : | semmle.label | hash1 [element :c] : | -| hash_flow.rb:430:12:434:7 | call to merge! [element :a] : | semmle.label | call to merge! [element :a] : | -| hash_flow.rb:430:12:434:7 | call to merge! [element :c] : | semmle.label | call to merge! [element :c] : | -| hash_flow.rb:430:12:434:7 | call to merge! [element :d] : | semmle.label | call to merge! [element :d] : | -| hash_flow.rb:430:12:434:7 | call to merge! [element :f] : | semmle.label | call to merge! [element :f] : | -| hash_flow.rb:430:25:430:29 | hash2 [element :d] : | semmle.label | hash2 [element :d] : | -| hash_flow.rb:430:25:430:29 | hash2 [element :f] : | semmle.label | hash2 [element :f] : | -| hash_flow.rb:430:41:430:49 | old_value : | semmle.label | old_value : | -| hash_flow.rb:430:52:430:60 | new_value : | semmle.label | new_value : | -| hash_flow.rb:432:14:432:22 | old_value | semmle.label | old_value | -| hash_flow.rb:433:14:433:22 | new_value | semmle.label | new_value | -| hash_flow.rb:435:10:435:19 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:435:11:435:14 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:435:11:435:18 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:437:10:437:19 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:437:11:437:14 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:437:11:437:18 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:438:10:438:19 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:438:11:438:14 | hash [element :d] : | semmle.label | hash [element :d] : | -| hash_flow.rb:438:11:438:18 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:440:10:440:19 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:440:11:440:14 | hash [element :f] : | semmle.label | hash [element :f] : | -| hash_flow.rb:440:11:440:18 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:442:10:442:20 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:442:11:442:15 | hash1 [element :a] : | semmle.label | hash1 [element :a] : | -| hash_flow.rb:442:11:442:19 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:444:10:444:20 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:444:11:444:15 | hash1 [element :c] : | semmle.label | hash1 [element :c] : | -| hash_flow.rb:444:11:444:19 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:445:10:445:20 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:445:11:445:15 | hash1 [element :d] : | semmle.label | hash1 [element :d] : | -| hash_flow.rb:445:11:445:19 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:447:10:447:20 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:447:11:447:15 | hash1 [element :f] : | semmle.label | hash1 [element :f] : | -| hash_flow.rb:447:11:447:19 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:454:15:454:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:457:9:457:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:457:9:457:22 | call to rassoc [element 1] : | semmle.label | call to rassoc [element 1] : | -| hash_flow.rb:459:10:459:10 | b [element 1] : | semmle.label | b [element 1] : | -| hash_flow.rb:459:10:459:13 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:466:15:466:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:469:9:469:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:469:9:473:7 | call to reject [element :a] : | semmle.label | call to reject [element :a] : | -| hash_flow.rb:469:29:469:33 | value : | semmle.label | value : | -| hash_flow.rb:471:14:471:18 | value | semmle.label | value | -| hash_flow.rb:474:10:474:10 | b [element :a] : | semmle.label | b [element :a] : | -| hash_flow.rb:474:10:474:14 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:481:15:481:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:484:9:484:12 | [post] hash [element :a] : | semmle.label | [post] hash [element :a] : | -| hash_flow.rb:484:9:484:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:484:9:488:7 | call to reject! [element :a] : | semmle.label | call to reject! [element :a] : | -| hash_flow.rb:484:30:484:34 | value : | semmle.label | value : | -| hash_flow.rb:486:14:486:18 | value | semmle.label | value | -| hash_flow.rb:489:10:489:10 | b [element :a] : | semmle.label | b [element :a] : | -| hash_flow.rb:489:10:489:14 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:490:10:490:13 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:490:10:490:17 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:497:15:497:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:499:15:499:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:504:5:504:9 | [post] hash2 [element :a] : | semmle.label | [post] hash2 [element :a] : | -| hash_flow.rb:504:5:504:9 | [post] hash2 [element :c] : | semmle.label | [post] hash2 [element :c] : | -| hash_flow.rb:504:19:504:22 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:504:19:504:22 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:505:10:505:20 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:505:11:505:15 | hash2 [element :a] : | semmle.label | hash2 [element :a] : | -| hash_flow.rb:505:11:505:19 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:507:10:507:20 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:507:11:507:15 | hash2 [element :c] : | semmle.label | hash2 [element :c] : | -| hash_flow.rb:507:11:507:19 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:512:15:512:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:514:15:514:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:516:9:516:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:516:9:516:12 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:516:9:520:7 | call to select [element :a] : | semmle.label | call to select [element :a] : | -| hash_flow.rb:516:30:516:34 | value : | semmle.label | value : | -| hash_flow.rb:518:14:518:18 | value | semmle.label | value | -| hash_flow.rb:521:10:521:16 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:521:11:521:11 | b [element :a] : | semmle.label | b [element :a] : | -| hash_flow.rb:521:11:521:15 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:528:15:528:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:530:15:530:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:532:5:532:8 | [post] hash [element :a] : | semmle.label | [post] hash [element :a] : | -| hash_flow.rb:532:5:532:8 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:532:5:532:8 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:532:27:532:31 | value : | semmle.label | value : | -| hash_flow.rb:534:14:534:18 | value | semmle.label | value | -| hash_flow.rb:537:10:537:19 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:537:11:537:14 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:537:11:537:18 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:544:15:544:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:546:15:546:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:548:9:548:12 | [post] hash [element :a] : | semmle.label | [post] hash [element :a] : | -| hash_flow.rb:548:9:548:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:548:9:548:12 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:548:9:548:18 | call to shift [element 1] : | semmle.label | call to shift [element 1] : | -| hash_flow.rb:549:10:549:19 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:549:11:549:14 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:549:11:549:18 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:551:10:551:15 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:551:11:551:11 | b [element 1] : | semmle.label | b [element 1] : | -| hash_flow.rb:551:11:551:14 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:558:15:558:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:560:15:560:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:562:9:562:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:562:9:562:26 | call to slice [element :a] : | semmle.label | call to slice [element :a] : | -| hash_flow.rb:563:10:563:16 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:563:11:563:11 | b [element :a] : | semmle.label | b [element :a] : | -| hash_flow.rb:563:11:563:15 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:567:9:567:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:567:9:567:12 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:567:9:567:25 | call to slice [element :a] : | semmle.label | call to slice [element :a] : | -| hash_flow.rb:567:9:567:25 | call to slice [element :c] : | semmle.label | call to slice [element :c] : | -| hash_flow.rb:568:10:568:16 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:568:11:568:11 | c [element :a] : | semmle.label | c [element :a] : | -| hash_flow.rb:568:11:568:15 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:570:10:570:16 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:570:11:570:11 | c [element :c] : | semmle.label | c [element :c] : | -| hash_flow.rb:570:11:570:15 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:577:15:577:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:579:15:579:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:581:9:581:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:581:9:581:12 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:581:9:581:17 | call to to_a [element, element 1] : | semmle.label | call to to_a [element, element 1] : | -| hash_flow.rb:583:10:583:18 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:583:11:583:11 | a [element, element 1] : | semmle.label | a [element, element 1] : | -| hash_flow.rb:583:11:583:14 | ...[...] [element 1] : | semmle.label | ...[...] [element 1] : | -| hash_flow.rb:583:11:583:17 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:590:15:590:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:592:15:592:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:594:9:594:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:594:9:594:12 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:594:9:594:17 | call to to_h [element :a] : | semmle.label | call to to_h [element :a] : | -| hash_flow.rb:594:9:594:17 | call to to_h [element :c] : | semmle.label | call to to_h [element :c] : | -| hash_flow.rb:595:10:595:16 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:595:11:595:11 | a [element :a] : | semmle.label | a [element :a] : | -| hash_flow.rb:595:11:595:15 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:597:10:597:16 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:597:11:597:11 | a [element :c] : | semmle.label | a [element :c] : | -| hash_flow.rb:597:11:597:15 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:599:9:599:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:599:9:599:12 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:599:9:603:7 | call to to_h [element] : | semmle.label | call to to_h [element] : | -| hash_flow.rb:599:28:599:32 | value : | semmle.label | value : | -| hash_flow.rb:601:14:601:18 | value | semmle.label | value | -| hash_flow.rb:602:14:602:24 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:604:10:604:16 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:604:11:604:11 | b [element] : | semmle.label | b [element] : | -| hash_flow.rb:604:11:604:15 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:611:15:611:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:613:15:613:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:615:9:615:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:615:9:615:12 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:615:9:615:45 | call to transform_keys [element] : | semmle.label | call to transform_keys [element] : | -| hash_flow.rb:616:10:616:17 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:616:11:616:11 | a [element] : | semmle.label | a [element] : | -| hash_flow.rb:616:11:616:16 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:617:10:617:17 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:617:11:617:11 | a [element] : | semmle.label | a [element] : | -| hash_flow.rb:617:11:617:16 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:618:10:618:17 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:618:11:618:11 | a [element] : | semmle.label | a [element] : | -| hash_flow.rb:618:11:618:16 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:625:15:625:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:627:15:627:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:629:5:629:8 | [post] hash [element] : | semmle.label | [post] hash [element] : | -| hash_flow.rb:629:5:629:8 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:629:5:629:8 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:630:10:630:20 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:630:11:630:14 | hash [element] : | semmle.label | hash [element] : | -| hash_flow.rb:630:11:630:19 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:631:10:631:20 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:631:11:631:14 | hash [element] : | semmle.label | hash [element] : | -| hash_flow.rb:631:11:631:19 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:632:10:632:20 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:632:11:632:14 | hash [element] : | semmle.label | hash [element] : | -| hash_flow.rb:632:11:632:19 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:639:15:639:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:641:15:641:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:643:9:643:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:643:9:643:12 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:643:9:646:7 | call to transform_values [element] : | semmle.label | call to transform_values [element] : | -| hash_flow.rb:643:35:643:39 | value : | semmle.label | value : | -| hash_flow.rb:644:14:644:18 | value | semmle.label | value | -| hash_flow.rb:645:9:645:19 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:647:10:647:19 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:647:11:647:14 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:647:11:647:18 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:648:10:648:16 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:648:11:648:11 | b [element] : | semmle.label | b [element] : | -| hash_flow.rb:648:11:648:15 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:655:15:655:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:657:15:657:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:659:5:659:8 | [post] hash [element] : | semmle.label | [post] hash [element] : | -| hash_flow.rb:659:5:659:8 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:659:5:659:8 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:659:32:659:36 | value : | semmle.label | value : | -| hash_flow.rb:660:14:660:18 | value | semmle.label | value | -| hash_flow.rb:661:9:661:19 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:663:10:663:19 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:663:11:663:14 | hash [element] : | semmle.label | hash [element] : | -| hash_flow.rb:663:11:663:18 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:670:15:670:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:672:15:672:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:675:15:675:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:677:15:677:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:679:12:679:16 | [post] hash1 [element :a] : | semmle.label | [post] hash1 [element :a] : | -| hash_flow.rb:679:12:679:16 | [post] hash1 [element :c] : | semmle.label | [post] hash1 [element :c] : | -| hash_flow.rb:679:12:679:16 | [post] hash1 [element :d] : | semmle.label | [post] hash1 [element :d] : | -| hash_flow.rb:679:12:679:16 | [post] hash1 [element :f] : | semmle.label | [post] hash1 [element :f] : | -| hash_flow.rb:679:12:679:16 | hash1 [element :a] : | semmle.label | hash1 [element :a] : | -| hash_flow.rb:679:12:679:16 | hash1 [element :c] : | semmle.label | hash1 [element :c] : | -| hash_flow.rb:679:12:683:7 | call to update [element :a] : | semmle.label | call to update [element :a] : | -| hash_flow.rb:679:12:683:7 | call to update [element :c] : | semmle.label | call to update [element :c] : | -| hash_flow.rb:679:12:683:7 | call to update [element :d] : | semmle.label | call to update [element :d] : | -| hash_flow.rb:679:12:683:7 | call to update [element :f] : | semmle.label | call to update [element :f] : | -| hash_flow.rb:679:25:679:29 | hash2 [element :d] : | semmle.label | hash2 [element :d] : | -| hash_flow.rb:679:25:679:29 | hash2 [element :f] : | semmle.label | hash2 [element :f] : | -| hash_flow.rb:679:41:679:49 | old_value : | semmle.label | old_value : | -| hash_flow.rb:679:52:679:60 | new_value : | semmle.label | new_value : | -| hash_flow.rb:681:14:681:22 | old_value | semmle.label | old_value | -| hash_flow.rb:682:14:682:22 | new_value | semmle.label | new_value | -| hash_flow.rb:684:10:684:19 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:684:11:684:14 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:684:11:684:18 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:686:10:686:19 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:686:11:686:14 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:686:11:686:18 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:687:10:687:19 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:687:11:687:14 | hash [element :d] : | semmle.label | hash [element :d] : | -| hash_flow.rb:687:11:687:18 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:689:10:689:19 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:689:11:689:14 | hash [element :f] : | semmle.label | hash [element :f] : | -| hash_flow.rb:689:11:689:18 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:691:10:691:20 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:691:11:691:15 | hash1 [element :a] : | semmle.label | hash1 [element :a] : | -| hash_flow.rb:691:11:691:19 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:693:10:693:20 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:693:11:693:15 | hash1 [element :c] : | semmle.label | hash1 [element :c] : | -| hash_flow.rb:693:11:693:19 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:694:10:694:20 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:694:11:694:15 | hash1 [element :d] : | semmle.label | hash1 [element :d] : | -| hash_flow.rb:694:11:694:19 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:696:10:696:20 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:696:11:696:15 | hash1 [element :f] : | semmle.label | hash1 [element :f] : | -| hash_flow.rb:696:11:696:19 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:703:15:703:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:705:15:705:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:707:9:707:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:707:9:707:12 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:707:9:707:19 | call to values [element] : | semmle.label | call to values [element] : | -| hash_flow.rb:708:10:708:15 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:708:11:708:11 | a [element] : | semmle.label | a [element] : | -| hash_flow.rb:708:11:708:14 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:715:15:715:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:717:15:717:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:719:9:719:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:719:9:719:26 | call to values_at [element 0] : | semmle.label | call to values_at [element 0] : | -| hash_flow.rb:720:10:720:10 | b [element 0] : | semmle.label | b [element 0] : | -| hash_flow.rb:720:10:720:13 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:721:9:721:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:721:9:721:12 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:721:9:721:31 | call to fetch_values [element] : | semmle.label | call to fetch_values [element] : | -| hash_flow.rb:722:10:722:10 | b [element] : | semmle.label | b [element] : | -| hash_flow.rb:722:10:722:13 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:729:15:729:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:731:15:731:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:734:15:734:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:736:15:736:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:738:14:738:20 | ** ... [element :a] : | semmle.label | ** ... [element :a] : | -| hash_flow.rb:738:14:738:20 | ** ... [element :c] : | semmle.label | ** ... [element :c] : | -| hash_flow.rb:738:16:738:20 | hash1 [element :a] : | semmle.label | hash1 [element :a] : | -| hash_flow.rb:738:16:738:20 | hash1 [element :c] : | semmle.label | hash1 [element :c] : | -| hash_flow.rb:738:29:738:39 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:738:42:738:48 | ** ... [element :d] : | semmle.label | ** ... [element :d] : | -| hash_flow.rb:738:42:738:48 | ** ... [element :f] : | semmle.label | ** ... [element :f] : | -| hash_flow.rb:738:44:738:48 | hash2 [element :d] : | semmle.label | hash2 [element :d] : | -| hash_flow.rb:738:44:738:48 | hash2 [element :f] : | semmle.label | hash2 [element :f] : | -| hash_flow.rb:739:10:739:13 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:739:10:739:17 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:741:10:741:13 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:741:10:741:17 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:742:10:742:13 | hash [element :d] : | semmle.label | hash [element :d] : | -| hash_flow.rb:742:10:742:17 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:744:10:744:13 | hash [element :f] : | semmle.label | hash [element :f] : | -| hash_flow.rb:744:10:744:17 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:745:10:745:13 | hash [element :g] : | semmle.label | hash [element :g] : | -| hash_flow.rb:745:10:745:17 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:309:9:309:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:309:9:309:22 | call to fetch : | semmle.label | call to fetch : | +| hash_flow.rb:310:10:310:10 | b | semmle.label | b | +| hash_flow.rb:311:9:311:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:311:9:311:35 | call to fetch : | semmle.label | call to fetch : | +| hash_flow.rb:311:24:311:34 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:312:10:312:10 | b | semmle.label | b | +| hash_flow.rb:313:9:313:35 | call to fetch : | semmle.label | call to fetch : | +| hash_flow.rb:313:24:313:34 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:314:10:314:10 | b | semmle.label | b | +| hash_flow.rb:315:9:315:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:315:9:315:12 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:315:9:315:34 | call to fetch : | semmle.label | call to fetch : | +| hash_flow.rb:315:23:315:33 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:316:10:316:10 | b | semmle.label | b | +| hash_flow.rb:323:15:323:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:325:15:325:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:327:9:327:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:327:9:327:12 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:327:9:330:7 | call to fetch_values [element] : | semmle.label | call to fetch_values [element] : | +| hash_flow.rb:327:27:327:37 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:327:44:327:44 | x : | semmle.label | x : | +| hash_flow.rb:328:14:328:14 | x | semmle.label | x | +| hash_flow.rb:329:9:329:19 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:331:10:331:10 | b [element] : | semmle.label | b [element] : | +| hash_flow.rb:331:10:331:13 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:332:9:332:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:332:9:332:29 | call to fetch_values [element] : | semmle.label | call to fetch_values [element] : | +| hash_flow.rb:333:10:333:10 | b [element] : | semmle.label | b [element] : | +| hash_flow.rb:333:10:333:13 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:334:9:334:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:334:9:334:12 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:334:9:334:31 | call to fetch_values [element] : | semmle.label | call to fetch_values [element] : | +| hash_flow.rb:335:10:335:10 | b [element] : | semmle.label | b [element] : | +| hash_flow.rb:335:10:335:13 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:342:15:342:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:344:15:344:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:346:9:346:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:346:9:346:12 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:346:9:350:7 | call to filter [element :a] : | semmle.label | call to filter [element :a] : | +| hash_flow.rb:346:30:346:34 | value : | semmle.label | value : | +| hash_flow.rb:348:14:348:18 | value | semmle.label | value | +| hash_flow.rb:351:10:351:16 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:351:11:351:11 | b [element :a] : | semmle.label | b [element :a] : | +| hash_flow.rb:351:11:351:15 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:358:15:358:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:360:15:360:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:362:5:362:8 | [post] hash [element :a] : | semmle.label | [post] hash [element :a] : | +| hash_flow.rb:362:5:362:8 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:362:5:362:8 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:362:27:362:31 | value : | semmle.label | value : | +| hash_flow.rb:364:14:364:18 | value | semmle.label | value | +| hash_flow.rb:367:10:367:19 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:367:11:367:14 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:367:11:367:18 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:374:15:374:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:376:15:376:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:378:9:378:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:378:9:378:12 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:378:9:378:20 | call to flatten [element] : | semmle.label | call to flatten [element] : | +| hash_flow.rb:379:10:379:15 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:379:11:379:11 | b [element] : | semmle.label | b [element] : | +| hash_flow.rb:379:11:379:14 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:386:15:386:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:388:15:388:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:390:9:390:12 | [post] hash [element :a] : | semmle.label | [post] hash [element :a] : | +| hash_flow.rb:390:9:390:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:390:9:390:12 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:390:9:394:7 | call to keep_if [element :a] : | semmle.label | call to keep_if [element :a] : | +| hash_flow.rb:390:31:390:35 | value : | semmle.label | value : | +| hash_flow.rb:392:14:392:18 | value | semmle.label | value | +| hash_flow.rb:395:10:395:19 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:395:11:395:14 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:395:11:395:18 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:396:10:396:16 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:396:11:396:11 | b [element :a] : | semmle.label | b [element :a] : | +| hash_flow.rb:396:11:396:15 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:403:15:403:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:405:15:405:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:408:15:408:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:410:15:410:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:412:12:412:16 | hash1 [element :a] : | semmle.label | hash1 [element :a] : | +| hash_flow.rb:412:12:412:16 | hash1 [element :c] : | semmle.label | hash1 [element :c] : | +| hash_flow.rb:412:12:416:7 | call to merge [element :a] : | semmle.label | call to merge [element :a] : | +| hash_flow.rb:412:12:416:7 | call to merge [element :c] : | semmle.label | call to merge [element :c] : | +| hash_flow.rb:412:12:416:7 | call to merge [element :d] : | semmle.label | call to merge [element :d] : | +| hash_flow.rb:412:12:416:7 | call to merge [element :f] : | semmle.label | call to merge [element :f] : | +| hash_flow.rb:412:24:412:28 | hash2 [element :d] : | semmle.label | hash2 [element :d] : | +| hash_flow.rb:412:24:412:28 | hash2 [element :f] : | semmle.label | hash2 [element :f] : | +| hash_flow.rb:412:40:412:48 | old_value : | semmle.label | old_value : | +| hash_flow.rb:412:51:412:59 | new_value : | semmle.label | new_value : | +| hash_flow.rb:414:14:414:22 | old_value | semmle.label | old_value | +| hash_flow.rb:415:14:415:22 | new_value | semmle.label | new_value | +| hash_flow.rb:417:10:417:19 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:417:11:417:14 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:417:11:417:18 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:419:10:419:19 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:419:11:419:14 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:419:11:419:18 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:420:10:420:19 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:420:11:420:14 | hash [element :d] : | semmle.label | hash [element :d] : | +| hash_flow.rb:420:11:420:18 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:422:10:422:19 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:422:11:422:14 | hash [element :f] : | semmle.label | hash [element :f] : | +| hash_flow.rb:422:11:422:18 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:429:15:429:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:431:15:431:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:434:15:434:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:436:15:436:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:438:12:438:16 | [post] hash1 [element :a] : | semmle.label | [post] hash1 [element :a] : | +| hash_flow.rb:438:12:438:16 | [post] hash1 [element :c] : | semmle.label | [post] hash1 [element :c] : | +| hash_flow.rb:438:12:438:16 | [post] hash1 [element :d] : | semmle.label | [post] hash1 [element :d] : | +| hash_flow.rb:438:12:438:16 | [post] hash1 [element :f] : | semmle.label | [post] hash1 [element :f] : | +| hash_flow.rb:438:12:438:16 | hash1 [element :a] : | semmle.label | hash1 [element :a] : | +| hash_flow.rb:438:12:438:16 | hash1 [element :c] : | semmle.label | hash1 [element :c] : | +| hash_flow.rb:438:12:442:7 | call to merge! [element :a] : | semmle.label | call to merge! [element :a] : | +| hash_flow.rb:438:12:442:7 | call to merge! [element :c] : | semmle.label | call to merge! [element :c] : | +| hash_flow.rb:438:12:442:7 | call to merge! [element :d] : | semmle.label | call to merge! [element :d] : | +| hash_flow.rb:438:12:442:7 | call to merge! [element :f] : | semmle.label | call to merge! [element :f] : | +| hash_flow.rb:438:25:438:29 | hash2 [element :d] : | semmle.label | hash2 [element :d] : | +| hash_flow.rb:438:25:438:29 | hash2 [element :f] : | semmle.label | hash2 [element :f] : | +| hash_flow.rb:438:41:438:49 | old_value : | semmle.label | old_value : | +| hash_flow.rb:438:52:438:60 | new_value : | semmle.label | new_value : | +| hash_flow.rb:440:14:440:22 | old_value | semmle.label | old_value | +| hash_flow.rb:441:14:441:22 | new_value | semmle.label | new_value | +| hash_flow.rb:443:10:443:19 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:443:11:443:14 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:443:11:443:18 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:445:10:445:19 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:445:11:445:14 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:445:11:445:18 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:446:10:446:19 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:446:11:446:14 | hash [element :d] : | semmle.label | hash [element :d] : | +| hash_flow.rb:446:11:446:18 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:448:10:448:19 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:448:11:448:14 | hash [element :f] : | semmle.label | hash [element :f] : | +| hash_flow.rb:448:11:448:18 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:450:10:450:20 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:450:11:450:15 | hash1 [element :a] : | semmle.label | hash1 [element :a] : | +| hash_flow.rb:450:11:450:19 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:452:10:452:20 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:452:11:452:15 | hash1 [element :c] : | semmle.label | hash1 [element :c] : | +| hash_flow.rb:452:11:452:19 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:453:10:453:20 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:453:11:453:15 | hash1 [element :d] : | semmle.label | hash1 [element :d] : | +| hash_flow.rb:453:11:453:19 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:455:10:455:20 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:455:11:455:15 | hash1 [element :f] : | semmle.label | hash1 [element :f] : | +| hash_flow.rb:455:11:455:19 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:462:15:462:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:465:9:465:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:465:9:465:22 | call to rassoc [element 1] : | semmle.label | call to rassoc [element 1] : | +| hash_flow.rb:467:10:467:10 | b [element 1] : | semmle.label | b [element 1] : | +| hash_flow.rb:467:10:467:13 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:474:15:474:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:477:9:477:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:477:9:481:7 | call to reject [element :a] : | semmle.label | call to reject [element :a] : | +| hash_flow.rb:477:29:477:33 | value : | semmle.label | value : | +| hash_flow.rb:479:14:479:18 | value | semmle.label | value | +| hash_flow.rb:482:10:482:10 | b [element :a] : | semmle.label | b [element :a] : | +| hash_flow.rb:482:10:482:14 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:489:15:489:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:492:9:492:12 | [post] hash [element :a] : | semmle.label | [post] hash [element :a] : | +| hash_flow.rb:492:9:492:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:492:9:496:7 | call to reject! [element :a] : | semmle.label | call to reject! [element :a] : | +| hash_flow.rb:492:30:492:34 | value : | semmle.label | value : | +| hash_flow.rb:494:14:494:18 | value | semmle.label | value | +| hash_flow.rb:497:10:497:10 | b [element :a] : | semmle.label | b [element :a] : | +| hash_flow.rb:497:10:497:14 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:498:10:498:13 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:498:10:498:17 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:505:15:505:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:507:15:507:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:512:5:512:9 | [post] hash2 [element :a] : | semmle.label | [post] hash2 [element :a] : | +| hash_flow.rb:512:5:512:9 | [post] hash2 [element :c] : | semmle.label | [post] hash2 [element :c] : | +| hash_flow.rb:512:19:512:22 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:512:19:512:22 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:513:10:513:20 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:513:11:513:15 | hash2 [element :a] : | semmle.label | hash2 [element :a] : | +| hash_flow.rb:513:11:513:19 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:515:10:515:20 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:515:11:515:15 | hash2 [element :c] : | semmle.label | hash2 [element :c] : | +| hash_flow.rb:515:11:515:19 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:520:15:520:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:522:15:522:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:524:9:524:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:524:9:524:12 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:524:9:528:7 | call to select [element :a] : | semmle.label | call to select [element :a] : | +| hash_flow.rb:524:30:524:34 | value : | semmle.label | value : | +| hash_flow.rb:526:14:526:18 | value | semmle.label | value | +| hash_flow.rb:529:10:529:16 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:529:11:529:11 | b [element :a] : | semmle.label | b [element :a] : | +| hash_flow.rb:529:11:529:15 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:536:15:536:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:538:15:538:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:540:5:540:8 | [post] hash [element :a] : | semmle.label | [post] hash [element :a] : | +| hash_flow.rb:540:5:540:8 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:540:5:540:8 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:540:27:540:31 | value : | semmle.label | value : | +| hash_flow.rb:542:14:542:18 | value | semmle.label | value | +| hash_flow.rb:545:10:545:19 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:545:11:545:14 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:545:11:545:18 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:552:15:552:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:554:15:554:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:556:9:556:12 | [post] hash [element :a] : | semmle.label | [post] hash [element :a] : | +| hash_flow.rb:556:9:556:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:556:9:556:12 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:556:9:556:18 | call to shift [element 1] : | semmle.label | call to shift [element 1] : | +| hash_flow.rb:557:10:557:19 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:557:11:557:14 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:557:11:557:18 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:559:10:559:15 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:559:11:559:11 | b [element 1] : | semmle.label | b [element 1] : | +| hash_flow.rb:559:11:559:14 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:566:15:566:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:568:15:568:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:570:9:570:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:570:9:570:26 | call to slice [element :a] : | semmle.label | call to slice [element :a] : | +| hash_flow.rb:571:10:571:16 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:571:11:571:11 | b [element :a] : | semmle.label | b [element :a] : | +| hash_flow.rb:571:11:571:15 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:575:9:575:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:575:9:575:12 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:575:9:575:25 | call to slice [element :a] : | semmle.label | call to slice [element :a] : | +| hash_flow.rb:575:9:575:25 | call to slice [element :c] : | semmle.label | call to slice [element :c] : | +| hash_flow.rb:576:10:576:16 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:576:11:576:11 | c [element :a] : | semmle.label | c [element :a] : | +| hash_flow.rb:576:11:576:15 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:578:10:578:16 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:578:11:578:11 | c [element :c] : | semmle.label | c [element :c] : | +| hash_flow.rb:578:11:578:15 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:585:15:585:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:587:15:587:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:589:9:589:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:589:9:589:12 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:589:9:589:17 | call to to_a [element, element 1] : | semmle.label | call to to_a [element, element 1] : | +| hash_flow.rb:591:10:591:18 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:591:11:591:11 | a [element, element 1] : | semmle.label | a [element, element 1] : | +| hash_flow.rb:591:11:591:14 | ...[...] [element 1] : | semmle.label | ...[...] [element 1] : | +| hash_flow.rb:591:11:591:17 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:598:15:598:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:600:15:600:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:602:9:602:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:602:9:602:12 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:602:9:602:17 | call to to_h [element :a] : | semmle.label | call to to_h [element :a] : | +| hash_flow.rb:602:9:602:17 | call to to_h [element :c] : | semmle.label | call to to_h [element :c] : | +| hash_flow.rb:603:10:603:16 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:603:11:603:11 | a [element :a] : | semmle.label | a [element :a] : | +| hash_flow.rb:603:11:603:15 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:605:10:605:16 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:605:11:605:11 | a [element :c] : | semmle.label | a [element :c] : | +| hash_flow.rb:605:11:605:15 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:607:9:607:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:607:9:607:12 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:607:9:611:7 | call to to_h [element] : | semmle.label | call to to_h [element] : | +| hash_flow.rb:607:28:607:32 | value : | semmle.label | value : | +| hash_flow.rb:609:14:609:18 | value | semmle.label | value | +| hash_flow.rb:610:14:610:24 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:612:10:612:16 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:612:11:612:11 | b [element] : | semmle.label | b [element] : | +| hash_flow.rb:612:11:612:15 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:619:15:619:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:621:15:621:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:623:9:623:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:623:9:623:12 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:623:9:623:45 | call to transform_keys [element] : | semmle.label | call to transform_keys [element] : | +| hash_flow.rb:624:10:624:17 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:624:11:624:11 | a [element] : | semmle.label | a [element] : | +| hash_flow.rb:624:11:624:16 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:625:10:625:17 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:625:11:625:11 | a [element] : | semmle.label | a [element] : | +| hash_flow.rb:625:11:625:16 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:626:10:626:17 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:626:11:626:11 | a [element] : | semmle.label | a [element] : | +| hash_flow.rb:626:11:626:16 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:633:15:633:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:635:15:635:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:637:5:637:8 | [post] hash [element] : | semmle.label | [post] hash [element] : | +| hash_flow.rb:637:5:637:8 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:637:5:637:8 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:638:10:638:20 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:638:11:638:14 | hash [element] : | semmle.label | hash [element] : | +| hash_flow.rb:638:11:638:19 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:639:10:639:20 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:639:11:639:14 | hash [element] : | semmle.label | hash [element] : | +| hash_flow.rb:639:11:639:19 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:640:10:640:20 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:640:11:640:14 | hash [element] : | semmle.label | hash [element] : | +| hash_flow.rb:640:11:640:19 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:647:15:647:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:649:15:649:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:651:9:651:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:651:9:651:12 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:651:9:654:7 | call to transform_values [element] : | semmle.label | call to transform_values [element] : | +| hash_flow.rb:651:35:651:39 | value : | semmle.label | value : | +| hash_flow.rb:652:14:652:18 | value | semmle.label | value | +| hash_flow.rb:653:9:653:19 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:655:10:655:19 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:655:11:655:14 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:655:11:655:18 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:656:10:656:16 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:656:11:656:11 | b [element] : | semmle.label | b [element] : | +| hash_flow.rb:656:11:656:15 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:663:15:663:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:665:15:665:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:667:5:667:8 | [post] hash [element] : | semmle.label | [post] hash [element] : | +| hash_flow.rb:667:5:667:8 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:667:5:667:8 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:667:32:667:36 | value : | semmle.label | value : | +| hash_flow.rb:668:14:668:18 | value | semmle.label | value | +| hash_flow.rb:669:9:669:19 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:671:10:671:19 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:671:11:671:14 | hash [element] : | semmle.label | hash [element] : | +| hash_flow.rb:671:11:671:18 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:678:15:678:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:680:15:680:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:683:15:683:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:685:15:685:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:687:12:687:16 | [post] hash1 [element :a] : | semmle.label | [post] hash1 [element :a] : | +| hash_flow.rb:687:12:687:16 | [post] hash1 [element :c] : | semmle.label | [post] hash1 [element :c] : | +| hash_flow.rb:687:12:687:16 | [post] hash1 [element :d] : | semmle.label | [post] hash1 [element :d] : | +| hash_flow.rb:687:12:687:16 | [post] hash1 [element :f] : | semmle.label | [post] hash1 [element :f] : | +| hash_flow.rb:687:12:687:16 | hash1 [element :a] : | semmle.label | hash1 [element :a] : | +| hash_flow.rb:687:12:687:16 | hash1 [element :c] : | semmle.label | hash1 [element :c] : | +| hash_flow.rb:687:12:691:7 | call to update [element :a] : | semmle.label | call to update [element :a] : | +| hash_flow.rb:687:12:691:7 | call to update [element :c] : | semmle.label | call to update [element :c] : | +| hash_flow.rb:687:12:691:7 | call to update [element :d] : | semmle.label | call to update [element :d] : | +| hash_flow.rb:687:12:691:7 | call to update [element :f] : | semmle.label | call to update [element :f] : | +| hash_flow.rb:687:25:687:29 | hash2 [element :d] : | semmle.label | hash2 [element :d] : | +| hash_flow.rb:687:25:687:29 | hash2 [element :f] : | semmle.label | hash2 [element :f] : | +| hash_flow.rb:687:41:687:49 | old_value : | semmle.label | old_value : | +| hash_flow.rb:687:52:687:60 | new_value : | semmle.label | new_value : | +| hash_flow.rb:689:14:689:22 | old_value | semmle.label | old_value | +| hash_flow.rb:690:14:690:22 | new_value | semmle.label | new_value | +| hash_flow.rb:692:10:692:19 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:692:11:692:14 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:692:11:692:18 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:694:10:694:19 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:694:11:694:14 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:694:11:694:18 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:695:10:695:19 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:695:11:695:14 | hash [element :d] : | semmle.label | hash [element :d] : | +| hash_flow.rb:695:11:695:18 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:697:10:697:19 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:697:11:697:14 | hash [element :f] : | semmle.label | hash [element :f] : | +| hash_flow.rb:697:11:697:18 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:699:10:699:20 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:699:11:699:15 | hash1 [element :a] : | semmle.label | hash1 [element :a] : | +| hash_flow.rb:699:11:699:19 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:701:10:701:20 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:701:11:701:15 | hash1 [element :c] : | semmle.label | hash1 [element :c] : | +| hash_flow.rb:701:11:701:19 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:702:10:702:20 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:702:11:702:15 | hash1 [element :d] : | semmle.label | hash1 [element :d] : | +| hash_flow.rb:702:11:702:19 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:704:10:704:20 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:704:11:704:15 | hash1 [element :f] : | semmle.label | hash1 [element :f] : | +| hash_flow.rb:704:11:704:19 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:711:15:711:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:713:15:713:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:715:9:715:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:715:9:715:12 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:715:9:715:19 | call to values [element] : | semmle.label | call to values [element] : | +| hash_flow.rb:716:10:716:15 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:716:11:716:11 | a [element] : | semmle.label | a [element] : | +| hash_flow.rb:716:11:716:14 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:723:15:723:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:725:15:725:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:727:9:727:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:727:9:727:26 | call to values_at [element 0] : | semmle.label | call to values_at [element 0] : | +| hash_flow.rb:728:10:728:10 | b [element 0] : | semmle.label | b [element 0] : | +| hash_flow.rb:728:10:728:13 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:729:9:729:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:729:9:729:12 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:729:9:729:31 | call to fetch_values [element] : | semmle.label | call to fetch_values [element] : | +| hash_flow.rb:730:10:730:10 | b [element] : | semmle.label | b [element] : | +| hash_flow.rb:730:10:730:13 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:737:15:737:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:739:15:739:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:742:15:742:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:744:15:744:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:746:14:746:20 | ** ... [element :a] : | semmle.label | ** ... [element :a] : | +| hash_flow.rb:746:14:746:20 | ** ... [element :c] : | semmle.label | ** ... [element :c] : | +| hash_flow.rb:746:16:746:20 | hash1 [element :a] : | semmle.label | hash1 [element :a] : | +| hash_flow.rb:746:16:746:20 | hash1 [element :c] : | semmle.label | hash1 [element :c] : | +| hash_flow.rb:746:29:746:39 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:746:42:746:48 | ** ... [element :d] : | semmle.label | ** ... [element :d] : | +| hash_flow.rb:746:42:746:48 | ** ... [element :f] : | semmle.label | ** ... [element :f] : | +| hash_flow.rb:746:44:746:48 | hash2 [element :d] : | semmle.label | hash2 [element :d] : | +| hash_flow.rb:746:44:746:48 | hash2 [element :f] : | semmle.label | hash2 [element :f] : | +| hash_flow.rb:747:10:747:13 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:747:10:747:17 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:749:10:749:13 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:749:10:749:17 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:750:10:750:13 | hash [element :d] : | semmle.label | hash [element :d] : | +| hash_flow.rb:750:10:750:17 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:752:10:752:13 | hash [element :f] : | semmle.label | hash [element :f] : | +| hash_flow.rb:752:10:752:17 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:753:10:753:13 | hash [element :g] : | semmle.label | hash [element :g] : | +| hash_flow.rb:753:10:753:17 | ...[...] | semmle.label | ...[...] | subpaths #select | hash_flow.rb:22:10:22:17 | ...[...] | hash_flow.rb:11:15:11:24 | call to taint : | hash_flow.rb:22:10:22:17 | ...[...] | $@ | hash_flow.rb:11:15:11:24 | call to taint : | call to taint : | @@ -1114,160 +1132,162 @@ subpaths | hash_flow.rb:65:10:65:18 | ...[...] | hash_flow.rb:64:24:64:33 | call to taint : | hash_flow.rb:65:10:65:18 | ...[...] | $@ | hash_flow.rb:64:24:64:33 | call to taint : | call to taint : | | hash_flow.rb:66:10:66:18 | ...[...] | hash_flow.rb:64:24:64:33 | call to taint : | hash_flow.rb:66:10:66:18 | ...[...] | $@ | hash_flow.rb:64:24:64:33 | call to taint : | call to taint : | | hash_flow.rb:69:10:69:18 | ...[...] | hash_flow.rb:68:22:68:31 | call to taint : | hash_flow.rb:69:10:69:18 | ...[...] | $@ | hash_flow.rb:68:22:68:31 | call to taint : | call to taint : | -| hash_flow.rb:77:10:77:18 | ...[...] | hash_flow.rb:76:26:76:35 | call to taint : | hash_flow.rb:77:10:77:18 | ...[...] | $@ | hash_flow.rb:76:26:76:35 | call to taint : | call to taint : | -| hash_flow.rb:89:10:89:18 | ...[...] | hash_flow.rb:85:15:85:24 | call to taint : | hash_flow.rb:89:10:89:18 | ...[...] | $@ | hash_flow.rb:85:15:85:24 | call to taint : | call to taint : | -| hash_flow.rb:98:10:98:10 | b | hash_flow.rb:97:21:97:30 | call to taint : | hash_flow.rb:98:10:98:10 | b | $@ | hash_flow.rb:97:21:97:30 | call to taint : | call to taint : | -| hash_flow.rb:106:10:106:17 | ...[...] | hash_flow.rb:105:24:105:33 | call to taint : | hash_flow.rb:106:10:106:17 | ...[...] | $@ | hash_flow.rb:105:24:105:33 | call to taint : | call to taint : | -| hash_flow.rb:107:10:107:10 | b | hash_flow.rb:105:24:105:33 | call to taint : | hash_flow.rb:107:10:107:10 | b | $@ | hash_flow.rb:105:24:105:33 | call to taint : | call to taint : | -| hash_flow.rb:111:10:111:17 | ...[...] | hash_flow.rb:110:23:110:32 | call to taint : | hash_flow.rb:111:10:111:17 | ...[...] | $@ | hash_flow.rb:110:23:110:32 | call to taint : | call to taint : | -| hash_flow.rb:112:10:112:17 | ...[...] | hash_flow.rb:110:23:110:32 | call to taint : | hash_flow.rb:112:10:112:17 | ...[...] | $@ | hash_flow.rb:110:23:110:32 | call to taint : | call to taint : | -| hash_flow.rb:113:10:113:10 | c | hash_flow.rb:110:23:110:32 | call to taint : | hash_flow.rb:113:10:113:10 | c | $@ | hash_flow.rb:110:23:110:32 | call to taint : | call to taint : | -| hash_flow.rb:124:14:124:25 | key_or_value | hash_flow.rb:120:15:120:24 | call to taint : | hash_flow.rb:124:14:124:25 | key_or_value | $@ | hash_flow.rb:120:15:120:24 | call to taint : | call to taint : | -| hash_flow.rb:128:14:128:18 | value | hash_flow.rb:120:15:120:24 | call to taint : | hash_flow.rb:128:14:128:18 | value | $@ | hash_flow.rb:120:15:120:24 | call to taint : | call to taint : | -| hash_flow.rb:141:10:141:13 | ...[...] | hash_flow.rb:136:15:136:25 | call to taint : | hash_flow.rb:141:10:141:13 | ...[...] | $@ | hash_flow.rb:136:15:136:25 | call to taint : | call to taint : | -| hash_flow.rb:142:10:142:13 | ...[...] | hash_flow.rb:136:15:136:25 | call to taint : | hash_flow.rb:142:10:142:13 | ...[...] | $@ | hash_flow.rb:136:15:136:25 | call to taint : | call to taint : | -| hash_flow.rb:144:10:144:13 | ...[...] | hash_flow.rb:136:15:136:25 | call to taint : | hash_flow.rb:144:10:144:13 | ...[...] | $@ | hash_flow.rb:136:15:136:25 | call to taint : | call to taint : | -| hash_flow.rb:166:10:166:14 | ...[...] | hash_flow.rb:162:15:162:25 | call to taint : | hash_flow.rb:166:10:166:14 | ...[...] | $@ | hash_flow.rb:162:15:162:25 | call to taint : | call to taint : | -| hash_flow.rb:178:10:178:10 | a | hash_flow.rb:174:15:174:25 | call to taint : | hash_flow.rb:178:10:178:10 | a | $@ | hash_flow.rb:174:15:174:25 | call to taint : | call to taint : | -| hash_flow.rb:191:14:191:18 | value | hash_flow.rb:186:15:186:25 | call to taint : | hash_flow.rb:191:14:191:18 | value | $@ | hash_flow.rb:186:15:186:25 | call to taint : | call to taint : | -| hash_flow.rb:193:10:193:14 | ...[...] | hash_flow.rb:186:15:186:25 | call to taint : | hash_flow.rb:193:10:193:14 | ...[...] | $@ | hash_flow.rb:186:15:186:25 | call to taint : | call to taint : | -| hash_flow.rb:194:10:194:17 | ...[...] | hash_flow.rb:186:15:186:25 | call to taint : | hash_flow.rb:194:10:194:17 | ...[...] | $@ | hash_flow.rb:186:15:186:25 | call to taint : | call to taint : | -| hash_flow.rb:209:10:209:21 | call to dig | hash_flow.rb:202:15:202:25 | call to taint : | hash_flow.rb:209:10:209:21 | call to dig | $@ | hash_flow.rb:202:15:202:25 | call to taint : | call to taint : | -| hash_flow.rb:211:10:211:24 | call to dig | hash_flow.rb:205:19:205:29 | call to taint : | hash_flow.rb:211:10:211:24 | call to dig | $@ | hash_flow.rb:205:19:205:29 | call to taint : | call to taint : | -| hash_flow.rb:224:14:224:18 | value | hash_flow.rb:219:15:219:25 | call to taint : | hash_flow.rb:224:14:224:18 | value | $@ | hash_flow.rb:219:15:219:25 | call to taint : | call to taint : | -| hash_flow.rb:226:10:226:14 | ...[...] | hash_flow.rb:219:15:219:25 | call to taint : | hash_flow.rb:226:10:226:14 | ...[...] | $@ | hash_flow.rb:219:15:219:25 | call to taint : | call to taint : | -| hash_flow.rb:240:10:240:14 | ...[...] | hash_flow.rb:234:15:234:25 | call to taint : | hash_flow.rb:240:10:240:14 | ...[...] | $@ | hash_flow.rb:234:15:234:25 | call to taint : | call to taint : | -| hash_flow.rb:253:14:253:18 | value | hash_flow.rb:248:15:248:25 | call to taint : | hash_flow.rb:253:14:253:18 | value | $@ | hash_flow.rb:248:15:248:25 | call to taint : | call to taint : | -| hash_flow.rb:255:10:255:14 | ...[...] | hash_flow.rb:248:15:248:25 | call to taint : | hash_flow.rb:255:10:255:14 | ...[...] | $@ | hash_flow.rb:248:15:248:25 | call to taint : | call to taint : | -| hash_flow.rb:267:14:267:18 | value | hash_flow.rb:263:15:263:25 | call to taint : | hash_flow.rb:267:14:267:18 | value | $@ | hash_flow.rb:263:15:263:25 | call to taint : | call to taint : | -| hash_flow.rb:269:10:269:14 | ...[...] | hash_flow.rb:263:15:263:25 | call to taint : | hash_flow.rb:269:10:269:14 | ...[...] | $@ | hash_flow.rb:263:15:263:25 | call to taint : | call to taint : | -| hash_flow.rb:285:10:285:14 | ...[...] | hash_flow.rb:279:15:279:25 | call to taint : | hash_flow.rb:285:10:285:14 | ...[...] | $@ | hash_flow.rb:279:15:279:25 | call to taint : | call to taint : | -| hash_flow.rb:298:14:298:14 | x | hash_flow.rb:297:20:297:30 | call to taint : | hash_flow.rb:298:14:298:14 | x | $@ | hash_flow.rb:297:20:297:30 | call to taint : | call to taint : | -| hash_flow.rb:300:10:300:10 | b | hash_flow.rb:293:15:293:25 | call to taint : | hash_flow.rb:300:10:300:10 | b | $@ | hash_flow.rb:293:15:293:25 | call to taint : | call to taint : | -| hash_flow.rb:300:10:300:10 | b | hash_flow.rb:295:15:295:25 | call to taint : | hash_flow.rb:300:10:300:10 | b | $@ | hash_flow.rb:295:15:295:25 | call to taint : | call to taint : | -| hash_flow.rb:302:10:302:10 | b | hash_flow.rb:293:15:293:25 | call to taint : | hash_flow.rb:302:10:302:10 | b | $@ | hash_flow.rb:293:15:293:25 | call to taint : | call to taint : | -| hash_flow.rb:304:10:304:10 | b | hash_flow.rb:293:15:293:25 | call to taint : | hash_flow.rb:304:10:304:10 | b | $@ | hash_flow.rb:293:15:293:25 | call to taint : | call to taint : | -| hash_flow.rb:304:10:304:10 | b | hash_flow.rb:303:24:303:34 | call to taint : | hash_flow.rb:304:10:304:10 | b | $@ | hash_flow.rb:303:24:303:34 | call to taint : | call to taint : | -| hash_flow.rb:306:10:306:10 | b | hash_flow.rb:305:24:305:34 | call to taint : | hash_flow.rb:306:10:306:10 | b | $@ | hash_flow.rb:305:24:305:34 | call to taint : | call to taint : | -| hash_flow.rb:308:10:308:10 | b | hash_flow.rb:293:15:293:25 | call to taint : | hash_flow.rb:308:10:308:10 | b | $@ | hash_flow.rb:293:15:293:25 | call to taint : | call to taint : | -| hash_flow.rb:308:10:308:10 | b | hash_flow.rb:295:15:295:25 | call to taint : | hash_flow.rb:308:10:308:10 | b | $@ | hash_flow.rb:295:15:295:25 | call to taint : | call to taint : | -| hash_flow.rb:308:10:308:10 | b | hash_flow.rb:307:23:307:33 | call to taint : | hash_flow.rb:308:10:308:10 | b | $@ | hash_flow.rb:307:23:307:33 | call to taint : | call to taint : | -| hash_flow.rb:320:14:320:14 | x | hash_flow.rb:319:27:319:37 | call to taint : | hash_flow.rb:320:14:320:14 | x | $@ | hash_flow.rb:319:27:319:37 | call to taint : | call to taint : | -| hash_flow.rb:323:10:323:13 | ...[...] | hash_flow.rb:315:15:315:25 | call to taint : | hash_flow.rb:323:10:323:13 | ...[...] | $@ | hash_flow.rb:315:15:315:25 | call to taint : | call to taint : | -| hash_flow.rb:323:10:323:13 | ...[...] | hash_flow.rb:317:15:317:25 | call to taint : | hash_flow.rb:323:10:323:13 | ...[...] | $@ | hash_flow.rb:317:15:317:25 | call to taint : | call to taint : | -| hash_flow.rb:323:10:323:13 | ...[...] | hash_flow.rb:321:9:321:19 | call to taint : | hash_flow.rb:323:10:323:13 | ...[...] | $@ | hash_flow.rb:321:9:321:19 | call to taint : | call to taint : | -| hash_flow.rb:325:10:325:13 | ...[...] | hash_flow.rb:315:15:315:25 | call to taint : | hash_flow.rb:325:10:325:13 | ...[...] | $@ | hash_flow.rb:315:15:315:25 | call to taint : | call to taint : | -| hash_flow.rb:327:10:327:13 | ...[...] | hash_flow.rb:315:15:315:25 | call to taint : | hash_flow.rb:327:10:327:13 | ...[...] | $@ | hash_flow.rb:315:15:315:25 | call to taint : | call to taint : | -| hash_flow.rb:327:10:327:13 | ...[...] | hash_flow.rb:317:15:317:25 | call to taint : | hash_flow.rb:327:10:327:13 | ...[...] | $@ | hash_flow.rb:317:15:317:25 | call to taint : | call to taint : | -| hash_flow.rb:340:14:340:18 | value | hash_flow.rb:334:15:334:25 | call to taint : | hash_flow.rb:340:14:340:18 | value | $@ | hash_flow.rb:334:15:334:25 | call to taint : | call to taint : | -| hash_flow.rb:340:14:340:18 | value | hash_flow.rb:336:15:336:25 | call to taint : | hash_flow.rb:340:14:340:18 | value | $@ | hash_flow.rb:336:15:336:25 | call to taint : | call to taint : | -| hash_flow.rb:343:10:343:16 | ( ... ) | hash_flow.rb:334:15:334:25 | call to taint : | hash_flow.rb:343:10:343:16 | ( ... ) | $@ | hash_flow.rb:334:15:334:25 | call to taint : | call to taint : | -| hash_flow.rb:356:14:356:18 | value | hash_flow.rb:350:15:350:25 | call to taint : | hash_flow.rb:356:14:356:18 | value | $@ | hash_flow.rb:350:15:350:25 | call to taint : | call to taint : | -| hash_flow.rb:356:14:356:18 | value | hash_flow.rb:352:15:352:25 | call to taint : | hash_flow.rb:356:14:356:18 | value | $@ | hash_flow.rb:352:15:352:25 | call to taint : | call to taint : | -| hash_flow.rb:359:10:359:19 | ( ... ) | hash_flow.rb:350:15:350:25 | call to taint : | hash_flow.rb:359:10:359:19 | ( ... ) | $@ | hash_flow.rb:350:15:350:25 | call to taint : | call to taint : | -| hash_flow.rb:371:10:371:15 | ( ... ) | hash_flow.rb:366:15:366:25 | call to taint : | hash_flow.rb:371:10:371:15 | ( ... ) | $@ | hash_flow.rb:366:15:366:25 | call to taint : | call to taint : | -| hash_flow.rb:371:10:371:15 | ( ... ) | hash_flow.rb:368:15:368:25 | call to taint : | hash_flow.rb:371:10:371:15 | ( ... ) | $@ | hash_flow.rb:368:15:368:25 | call to taint : | call to taint : | -| hash_flow.rb:384:14:384:18 | value | hash_flow.rb:378:15:378:25 | call to taint : | hash_flow.rb:384:14:384:18 | value | $@ | hash_flow.rb:378:15:378:25 | call to taint : | call to taint : | -| hash_flow.rb:384:14:384:18 | value | hash_flow.rb:380:15:380:25 | call to taint : | hash_flow.rb:384:14:384:18 | value | $@ | hash_flow.rb:380:15:380:25 | call to taint : | call to taint : | -| hash_flow.rb:387:10:387:19 | ( ... ) | hash_flow.rb:378:15:378:25 | call to taint : | hash_flow.rb:387:10:387:19 | ( ... ) | $@ | hash_flow.rb:378:15:378:25 | call to taint : | call to taint : | -| hash_flow.rb:388:10:388:16 | ( ... ) | hash_flow.rb:378:15:378:25 | call to taint : | hash_flow.rb:388:10:388:16 | ( ... ) | $@ | hash_flow.rb:378:15:378:25 | call to taint : | call to taint : | -| hash_flow.rb:406:14:406:22 | old_value | hash_flow.rb:395:15:395:25 | call to taint : | hash_flow.rb:406:14:406:22 | old_value | $@ | hash_flow.rb:395:15:395:25 | call to taint : | call to taint : | -| hash_flow.rb:406:14:406:22 | old_value | hash_flow.rb:397:15:397:25 | call to taint : | hash_flow.rb:406:14:406:22 | old_value | $@ | hash_flow.rb:397:15:397:25 | call to taint : | call to taint : | -| hash_flow.rb:406:14:406:22 | old_value | hash_flow.rb:400:15:400:25 | call to taint : | hash_flow.rb:406:14:406:22 | old_value | $@ | hash_flow.rb:400:15:400:25 | call to taint : | call to taint : | -| hash_flow.rb:406:14:406:22 | old_value | hash_flow.rb:402:15:402:25 | call to taint : | hash_flow.rb:406:14:406:22 | old_value | $@ | hash_flow.rb:402:15:402:25 | call to taint : | call to taint : | -| hash_flow.rb:407:14:407:22 | new_value | hash_flow.rb:395:15:395:25 | call to taint : | hash_flow.rb:407:14:407:22 | new_value | $@ | hash_flow.rb:395:15:395:25 | call to taint : | call to taint : | -| hash_flow.rb:407:14:407:22 | new_value | hash_flow.rb:397:15:397:25 | call to taint : | hash_flow.rb:407:14:407:22 | new_value | $@ | hash_flow.rb:397:15:397:25 | call to taint : | call to taint : | -| hash_flow.rb:407:14:407:22 | new_value | hash_flow.rb:400:15:400:25 | call to taint : | hash_flow.rb:407:14:407:22 | new_value | $@ | hash_flow.rb:400:15:400:25 | call to taint : | call to taint : | -| hash_flow.rb:407:14:407:22 | new_value | hash_flow.rb:402:15:402:25 | call to taint : | hash_flow.rb:407:14:407:22 | new_value | $@ | hash_flow.rb:402:15:402:25 | call to taint : | call to taint : | -| hash_flow.rb:409:10:409:19 | ( ... ) | hash_flow.rb:395:15:395:25 | call to taint : | hash_flow.rb:409:10:409:19 | ( ... ) | $@ | hash_flow.rb:395:15:395:25 | call to taint : | call to taint : | -| hash_flow.rb:411:10:411:19 | ( ... ) | hash_flow.rb:397:15:397:25 | call to taint : | hash_flow.rb:411:10:411:19 | ( ... ) | $@ | hash_flow.rb:397:15:397:25 | call to taint : | call to taint : | -| hash_flow.rb:412:10:412:19 | ( ... ) | hash_flow.rb:400:15:400:25 | call to taint : | hash_flow.rb:412:10:412:19 | ( ... ) | $@ | hash_flow.rb:400:15:400:25 | call to taint : | call to taint : | -| hash_flow.rb:414:10:414:19 | ( ... ) | hash_flow.rb:402:15:402:25 | call to taint : | hash_flow.rb:414:10:414:19 | ( ... ) | $@ | hash_flow.rb:402:15:402:25 | call to taint : | call to taint : | -| hash_flow.rb:432:14:432:22 | old_value | hash_flow.rb:421:15:421:25 | call to taint : | hash_flow.rb:432:14:432:22 | old_value | $@ | hash_flow.rb:421:15:421:25 | call to taint : | call to taint : | -| hash_flow.rb:432:14:432:22 | old_value | hash_flow.rb:423:15:423:25 | call to taint : | hash_flow.rb:432:14:432:22 | old_value | $@ | hash_flow.rb:423:15:423:25 | call to taint : | call to taint : | -| hash_flow.rb:432:14:432:22 | old_value | hash_flow.rb:426:15:426:25 | call to taint : | hash_flow.rb:432:14:432:22 | old_value | $@ | hash_flow.rb:426:15:426:25 | call to taint : | call to taint : | -| hash_flow.rb:432:14:432:22 | old_value | hash_flow.rb:428:15:428:25 | call to taint : | hash_flow.rb:432:14:432:22 | old_value | $@ | hash_flow.rb:428:15:428:25 | call to taint : | call to taint : | -| hash_flow.rb:433:14:433:22 | new_value | hash_flow.rb:421:15:421:25 | call to taint : | hash_flow.rb:433:14:433:22 | new_value | $@ | hash_flow.rb:421:15:421:25 | call to taint : | call to taint : | -| hash_flow.rb:433:14:433:22 | new_value | hash_flow.rb:423:15:423:25 | call to taint : | hash_flow.rb:433:14:433:22 | new_value | $@ | hash_flow.rb:423:15:423:25 | call to taint : | call to taint : | -| hash_flow.rb:433:14:433:22 | new_value | hash_flow.rb:426:15:426:25 | call to taint : | hash_flow.rb:433:14:433:22 | new_value | $@ | hash_flow.rb:426:15:426:25 | call to taint : | call to taint : | -| hash_flow.rb:433:14:433:22 | new_value | hash_flow.rb:428:15:428:25 | call to taint : | hash_flow.rb:433:14:433:22 | new_value | $@ | hash_flow.rb:428:15:428:25 | call to taint : | call to taint : | -| hash_flow.rb:435:10:435:19 | ( ... ) | hash_flow.rb:421:15:421:25 | call to taint : | hash_flow.rb:435:10:435:19 | ( ... ) | $@ | hash_flow.rb:421:15:421:25 | call to taint : | call to taint : | -| hash_flow.rb:437:10:437:19 | ( ... ) | hash_flow.rb:423:15:423:25 | call to taint : | hash_flow.rb:437:10:437:19 | ( ... ) | $@ | hash_flow.rb:423:15:423:25 | call to taint : | call to taint : | -| hash_flow.rb:438:10:438:19 | ( ... ) | hash_flow.rb:426:15:426:25 | call to taint : | hash_flow.rb:438:10:438:19 | ( ... ) | $@ | hash_flow.rb:426:15:426:25 | call to taint : | call to taint : | -| hash_flow.rb:440:10:440:19 | ( ... ) | hash_flow.rb:428:15:428:25 | call to taint : | hash_flow.rb:440:10:440:19 | ( ... ) | $@ | hash_flow.rb:428:15:428:25 | call to taint : | call to taint : | -| hash_flow.rb:442:10:442:20 | ( ... ) | hash_flow.rb:421:15:421:25 | call to taint : | hash_flow.rb:442:10:442:20 | ( ... ) | $@ | hash_flow.rb:421:15:421:25 | call to taint : | call to taint : | -| hash_flow.rb:444:10:444:20 | ( ... ) | hash_flow.rb:423:15:423:25 | call to taint : | hash_flow.rb:444:10:444:20 | ( ... ) | $@ | hash_flow.rb:423:15:423:25 | call to taint : | call to taint : | -| hash_flow.rb:445:10:445:20 | ( ... ) | hash_flow.rb:426:15:426:25 | call to taint : | hash_flow.rb:445:10:445:20 | ( ... ) | $@ | hash_flow.rb:426:15:426:25 | call to taint : | call to taint : | -| hash_flow.rb:447:10:447:20 | ( ... ) | hash_flow.rb:428:15:428:25 | call to taint : | hash_flow.rb:447:10:447:20 | ( ... ) | $@ | hash_flow.rb:428:15:428:25 | call to taint : | call to taint : | -| hash_flow.rb:459:10:459:13 | ...[...] | hash_flow.rb:454:15:454:25 | call to taint : | hash_flow.rb:459:10:459:13 | ...[...] | $@ | hash_flow.rb:454:15:454:25 | call to taint : | call to taint : | -| hash_flow.rb:471:14:471:18 | value | hash_flow.rb:466:15:466:25 | call to taint : | hash_flow.rb:471:14:471:18 | value | $@ | hash_flow.rb:466:15:466:25 | call to taint : | call to taint : | -| hash_flow.rb:474:10:474:14 | ...[...] | hash_flow.rb:466:15:466:25 | call to taint : | hash_flow.rb:474:10:474:14 | ...[...] | $@ | hash_flow.rb:466:15:466:25 | call to taint : | call to taint : | -| hash_flow.rb:486:14:486:18 | value | hash_flow.rb:481:15:481:25 | call to taint : | hash_flow.rb:486:14:486:18 | value | $@ | hash_flow.rb:481:15:481:25 | call to taint : | call to taint : | -| hash_flow.rb:489:10:489:14 | ...[...] | hash_flow.rb:481:15:481:25 | call to taint : | hash_flow.rb:489:10:489:14 | ...[...] | $@ | hash_flow.rb:481:15:481:25 | call to taint : | call to taint : | -| hash_flow.rb:490:10:490:17 | ...[...] | hash_flow.rb:481:15:481:25 | call to taint : | hash_flow.rb:490:10:490:17 | ...[...] | $@ | hash_flow.rb:481:15:481:25 | call to taint : | call to taint : | -| hash_flow.rb:505:10:505:20 | ( ... ) | hash_flow.rb:497:15:497:25 | call to taint : | hash_flow.rb:505:10:505:20 | ( ... ) | $@ | hash_flow.rb:497:15:497:25 | call to taint : | call to taint : | -| hash_flow.rb:507:10:507:20 | ( ... ) | hash_flow.rb:499:15:499:25 | call to taint : | hash_flow.rb:507:10:507:20 | ( ... ) | $@ | hash_flow.rb:499:15:499:25 | call to taint : | call to taint : | -| hash_flow.rb:518:14:518:18 | value | hash_flow.rb:512:15:512:25 | call to taint : | hash_flow.rb:518:14:518:18 | value | $@ | hash_flow.rb:512:15:512:25 | call to taint : | call to taint : | -| hash_flow.rb:518:14:518:18 | value | hash_flow.rb:514:15:514:25 | call to taint : | hash_flow.rb:518:14:518:18 | value | $@ | hash_flow.rb:514:15:514:25 | call to taint : | call to taint : | -| hash_flow.rb:521:10:521:16 | ( ... ) | hash_flow.rb:512:15:512:25 | call to taint : | hash_flow.rb:521:10:521:16 | ( ... ) | $@ | hash_flow.rb:512:15:512:25 | call to taint : | call to taint : | -| hash_flow.rb:534:14:534:18 | value | hash_flow.rb:528:15:528:25 | call to taint : | hash_flow.rb:534:14:534:18 | value | $@ | hash_flow.rb:528:15:528:25 | call to taint : | call to taint : | -| hash_flow.rb:534:14:534:18 | value | hash_flow.rb:530:15:530:25 | call to taint : | hash_flow.rb:534:14:534:18 | value | $@ | hash_flow.rb:530:15:530:25 | call to taint : | call to taint : | -| hash_flow.rb:537:10:537:19 | ( ... ) | hash_flow.rb:528:15:528:25 | call to taint : | hash_flow.rb:537:10:537:19 | ( ... ) | $@ | hash_flow.rb:528:15:528:25 | call to taint : | call to taint : | -| hash_flow.rb:549:10:549:19 | ( ... ) | hash_flow.rb:544:15:544:25 | call to taint : | hash_flow.rb:549:10:549:19 | ( ... ) | $@ | hash_flow.rb:544:15:544:25 | call to taint : | call to taint : | -| hash_flow.rb:551:10:551:15 | ( ... ) | hash_flow.rb:544:15:544:25 | call to taint : | hash_flow.rb:551:10:551:15 | ( ... ) | $@ | hash_flow.rb:544:15:544:25 | call to taint : | call to taint : | -| hash_flow.rb:551:10:551:15 | ( ... ) | hash_flow.rb:546:15:546:25 | call to taint : | hash_flow.rb:551:10:551:15 | ( ... ) | $@ | hash_flow.rb:546:15:546:25 | call to taint : | call to taint : | -| hash_flow.rb:563:10:563:16 | ( ... ) | hash_flow.rb:558:15:558:25 | call to taint : | hash_flow.rb:563:10:563:16 | ( ... ) | $@ | hash_flow.rb:558:15:558:25 | call to taint : | call to taint : | -| hash_flow.rb:568:10:568:16 | ( ... ) | hash_flow.rb:558:15:558:25 | call to taint : | hash_flow.rb:568:10:568:16 | ( ... ) | $@ | hash_flow.rb:558:15:558:25 | call to taint : | call to taint : | -| hash_flow.rb:570:10:570:16 | ( ... ) | hash_flow.rb:560:15:560:25 | call to taint : | hash_flow.rb:570:10:570:16 | ( ... ) | $@ | hash_flow.rb:560:15:560:25 | call to taint : | call to taint : | -| hash_flow.rb:583:10:583:18 | ( ... ) | hash_flow.rb:577:15:577:25 | call to taint : | hash_flow.rb:583:10:583:18 | ( ... ) | $@ | hash_flow.rb:577:15:577:25 | call to taint : | call to taint : | -| hash_flow.rb:583:10:583:18 | ( ... ) | hash_flow.rb:579:15:579:25 | call to taint : | hash_flow.rb:583:10:583:18 | ( ... ) | $@ | hash_flow.rb:579:15:579:25 | call to taint : | call to taint : | -| hash_flow.rb:595:10:595:16 | ( ... ) | hash_flow.rb:590:15:590:25 | call to taint : | hash_flow.rb:595:10:595:16 | ( ... ) | $@ | hash_flow.rb:590:15:590:25 | call to taint : | call to taint : | -| hash_flow.rb:597:10:597:16 | ( ... ) | hash_flow.rb:592:15:592:25 | call to taint : | hash_flow.rb:597:10:597:16 | ( ... ) | $@ | hash_flow.rb:592:15:592:25 | call to taint : | call to taint : | -| hash_flow.rb:601:14:601:18 | value | hash_flow.rb:590:15:590:25 | call to taint : | hash_flow.rb:601:14:601:18 | value | $@ | hash_flow.rb:590:15:590:25 | call to taint : | call to taint : | -| hash_flow.rb:601:14:601:18 | value | hash_flow.rb:592:15:592:25 | call to taint : | hash_flow.rb:601:14:601:18 | value | $@ | hash_flow.rb:592:15:592:25 | call to taint : | call to taint : | -| hash_flow.rb:604:10:604:16 | ( ... ) | hash_flow.rb:602:14:602:24 | call to taint : | hash_flow.rb:604:10:604:16 | ( ... ) | $@ | hash_flow.rb:602:14:602:24 | call to taint : | call to taint : | -| hash_flow.rb:616:10:616:17 | ( ... ) | hash_flow.rb:611:15:611:25 | call to taint : | hash_flow.rb:616:10:616:17 | ( ... ) | $@ | hash_flow.rb:611:15:611:25 | call to taint : | call to taint : | -| hash_flow.rb:616:10:616:17 | ( ... ) | hash_flow.rb:613:15:613:25 | call to taint : | hash_flow.rb:616:10:616:17 | ( ... ) | $@ | hash_flow.rb:613:15:613:25 | call to taint : | call to taint : | -| hash_flow.rb:617:10:617:17 | ( ... ) | hash_flow.rb:611:15:611:25 | call to taint : | hash_flow.rb:617:10:617:17 | ( ... ) | $@ | hash_flow.rb:611:15:611:25 | call to taint : | call to taint : | -| hash_flow.rb:617:10:617:17 | ( ... ) | hash_flow.rb:613:15:613:25 | call to taint : | hash_flow.rb:617:10:617:17 | ( ... ) | $@ | hash_flow.rb:613:15:613:25 | call to taint : | call to taint : | -| hash_flow.rb:618:10:618:17 | ( ... ) | hash_flow.rb:611:15:611:25 | call to taint : | hash_flow.rb:618:10:618:17 | ( ... ) | $@ | hash_flow.rb:611:15:611:25 | call to taint : | call to taint : | -| hash_flow.rb:618:10:618:17 | ( ... ) | hash_flow.rb:613:15:613:25 | call to taint : | hash_flow.rb:618:10:618:17 | ( ... ) | $@ | hash_flow.rb:613:15:613:25 | call to taint : | call to taint : | -| hash_flow.rb:630:10:630:20 | ( ... ) | hash_flow.rb:625:15:625:25 | call to taint : | hash_flow.rb:630:10:630:20 | ( ... ) | $@ | hash_flow.rb:625:15:625:25 | call to taint : | call to taint : | -| hash_flow.rb:630:10:630:20 | ( ... ) | hash_flow.rb:627:15:627:25 | call to taint : | hash_flow.rb:630:10:630:20 | ( ... ) | $@ | hash_flow.rb:627:15:627:25 | call to taint : | call to taint : | -| hash_flow.rb:631:10:631:20 | ( ... ) | hash_flow.rb:625:15:625:25 | call to taint : | hash_flow.rb:631:10:631:20 | ( ... ) | $@ | hash_flow.rb:625:15:625:25 | call to taint : | call to taint : | -| hash_flow.rb:631:10:631:20 | ( ... ) | hash_flow.rb:627:15:627:25 | call to taint : | hash_flow.rb:631:10:631:20 | ( ... ) | $@ | hash_flow.rb:627:15:627:25 | call to taint : | call to taint : | -| hash_flow.rb:632:10:632:20 | ( ... ) | hash_flow.rb:625:15:625:25 | call to taint : | hash_flow.rb:632:10:632:20 | ( ... ) | $@ | hash_flow.rb:625:15:625:25 | call to taint : | call to taint : | -| hash_flow.rb:632:10:632:20 | ( ... ) | hash_flow.rb:627:15:627:25 | call to taint : | hash_flow.rb:632:10:632:20 | ( ... ) | $@ | hash_flow.rb:627:15:627:25 | call to taint : | call to taint : | -| hash_flow.rb:644:14:644:18 | value | hash_flow.rb:639:15:639:25 | call to taint : | hash_flow.rb:644:14:644:18 | value | $@ | hash_flow.rb:639:15:639:25 | call to taint : | call to taint : | -| hash_flow.rb:644:14:644:18 | value | hash_flow.rb:641:15:641:25 | call to taint : | hash_flow.rb:644:14:644:18 | value | $@ | hash_flow.rb:641:15:641:25 | call to taint : | call to taint : | -| hash_flow.rb:647:10:647:19 | ( ... ) | hash_flow.rb:639:15:639:25 | call to taint : | hash_flow.rb:647:10:647:19 | ( ... ) | $@ | hash_flow.rb:639:15:639:25 | call to taint : | call to taint : | -| hash_flow.rb:648:10:648:16 | ( ... ) | hash_flow.rb:645:9:645:19 | call to taint : | hash_flow.rb:648:10:648:16 | ( ... ) | $@ | hash_flow.rb:645:9:645:19 | call to taint : | call to taint : | -| hash_flow.rb:660:14:660:18 | value | hash_flow.rb:655:15:655:25 | call to taint : | hash_flow.rb:660:14:660:18 | value | $@ | hash_flow.rb:655:15:655:25 | call to taint : | call to taint : | -| hash_flow.rb:660:14:660:18 | value | hash_flow.rb:657:15:657:25 | call to taint : | hash_flow.rb:660:14:660:18 | value | $@ | hash_flow.rb:657:15:657:25 | call to taint : | call to taint : | -| hash_flow.rb:663:10:663:19 | ( ... ) | hash_flow.rb:661:9:661:19 | call to taint : | hash_flow.rb:663:10:663:19 | ( ... ) | $@ | hash_flow.rb:661:9:661:19 | call to taint : | call to taint : | -| hash_flow.rb:681:14:681:22 | old_value | hash_flow.rb:670:15:670:25 | call to taint : | hash_flow.rb:681:14:681:22 | old_value | $@ | hash_flow.rb:670:15:670:25 | call to taint : | call to taint : | -| hash_flow.rb:681:14:681:22 | old_value | hash_flow.rb:672:15:672:25 | call to taint : | hash_flow.rb:681:14:681:22 | old_value | $@ | hash_flow.rb:672:15:672:25 | call to taint : | call to taint : | -| hash_flow.rb:681:14:681:22 | old_value | hash_flow.rb:675:15:675:25 | call to taint : | hash_flow.rb:681:14:681:22 | old_value | $@ | hash_flow.rb:675:15:675:25 | call to taint : | call to taint : | -| hash_flow.rb:681:14:681:22 | old_value | hash_flow.rb:677:15:677:25 | call to taint : | hash_flow.rb:681:14:681:22 | old_value | $@ | hash_flow.rb:677:15:677:25 | call to taint : | call to taint : | -| hash_flow.rb:682:14:682:22 | new_value | hash_flow.rb:670:15:670:25 | call to taint : | hash_flow.rb:682:14:682:22 | new_value | $@ | hash_flow.rb:670:15:670:25 | call to taint : | call to taint : | -| hash_flow.rb:682:14:682:22 | new_value | hash_flow.rb:672:15:672:25 | call to taint : | hash_flow.rb:682:14:682:22 | new_value | $@ | hash_flow.rb:672:15:672:25 | call to taint : | call to taint : | -| hash_flow.rb:682:14:682:22 | new_value | hash_flow.rb:675:15:675:25 | call to taint : | hash_flow.rb:682:14:682:22 | new_value | $@ | hash_flow.rb:675:15:675:25 | call to taint : | call to taint : | -| hash_flow.rb:682:14:682:22 | new_value | hash_flow.rb:677:15:677:25 | call to taint : | hash_flow.rb:682:14:682:22 | new_value | $@ | hash_flow.rb:677:15:677:25 | call to taint : | call to taint : | -| hash_flow.rb:684:10:684:19 | ( ... ) | hash_flow.rb:670:15:670:25 | call to taint : | hash_flow.rb:684:10:684:19 | ( ... ) | $@ | hash_flow.rb:670:15:670:25 | call to taint : | call to taint : | -| hash_flow.rb:686:10:686:19 | ( ... ) | hash_flow.rb:672:15:672:25 | call to taint : | hash_flow.rb:686:10:686:19 | ( ... ) | $@ | hash_flow.rb:672:15:672:25 | call to taint : | call to taint : | -| hash_flow.rb:687:10:687:19 | ( ... ) | hash_flow.rb:675:15:675:25 | call to taint : | hash_flow.rb:687:10:687:19 | ( ... ) | $@ | hash_flow.rb:675:15:675:25 | call to taint : | call to taint : | -| hash_flow.rb:689:10:689:19 | ( ... ) | hash_flow.rb:677:15:677:25 | call to taint : | hash_flow.rb:689:10:689:19 | ( ... ) | $@ | hash_flow.rb:677:15:677:25 | call to taint : | call to taint : | -| hash_flow.rb:691:10:691:20 | ( ... ) | hash_flow.rb:670:15:670:25 | call to taint : | hash_flow.rb:691:10:691:20 | ( ... ) | $@ | hash_flow.rb:670:15:670:25 | call to taint : | call to taint : | -| hash_flow.rb:693:10:693:20 | ( ... ) | hash_flow.rb:672:15:672:25 | call to taint : | hash_flow.rb:693:10:693:20 | ( ... ) | $@ | hash_flow.rb:672:15:672:25 | call to taint : | call to taint : | -| hash_flow.rb:694:10:694:20 | ( ... ) | hash_flow.rb:675:15:675:25 | call to taint : | hash_flow.rb:694:10:694:20 | ( ... ) | $@ | hash_flow.rb:675:15:675:25 | call to taint : | call to taint : | -| hash_flow.rb:696:10:696:20 | ( ... ) | hash_flow.rb:677:15:677:25 | call to taint : | hash_flow.rb:696:10:696:20 | ( ... ) | $@ | hash_flow.rb:677:15:677:25 | call to taint : | call to taint : | -| hash_flow.rb:708:10:708:15 | ( ... ) | hash_flow.rb:703:15:703:25 | call to taint : | hash_flow.rb:708:10:708:15 | ( ... ) | $@ | hash_flow.rb:703:15:703:25 | call to taint : | call to taint : | -| hash_flow.rb:708:10:708:15 | ( ... ) | hash_flow.rb:705:15:705:25 | call to taint : | hash_flow.rb:708:10:708:15 | ( ... ) | $@ | hash_flow.rb:705:15:705:25 | call to taint : | call to taint : | -| hash_flow.rb:720:10:720:13 | ...[...] | hash_flow.rb:715:15:715:25 | call to taint : | hash_flow.rb:720:10:720:13 | ...[...] | $@ | hash_flow.rb:715:15:715:25 | call to taint : | call to taint : | -| hash_flow.rb:722:10:722:13 | ...[...] | hash_flow.rb:715:15:715:25 | call to taint : | hash_flow.rb:722:10:722:13 | ...[...] | $@ | hash_flow.rb:715:15:715:25 | call to taint : | call to taint : | -| hash_flow.rb:722:10:722:13 | ...[...] | hash_flow.rb:717:15:717:25 | call to taint : | hash_flow.rb:722:10:722:13 | ...[...] | $@ | hash_flow.rb:717:15:717:25 | call to taint : | call to taint : | -| hash_flow.rb:739:10:739:17 | ...[...] | hash_flow.rb:729:15:729:25 | call to taint : | hash_flow.rb:739:10:739:17 | ...[...] | $@ | hash_flow.rb:729:15:729:25 | call to taint : | call to taint : | -| hash_flow.rb:741:10:741:17 | ...[...] | hash_flow.rb:731:15:731:25 | call to taint : | hash_flow.rb:741:10:741:17 | ...[...] | $@ | hash_flow.rb:731:15:731:25 | call to taint : | call to taint : | -| hash_flow.rb:742:10:742:17 | ...[...] | hash_flow.rb:734:15:734:25 | call to taint : | hash_flow.rb:742:10:742:17 | ...[...] | $@ | hash_flow.rb:734:15:734:25 | call to taint : | call to taint : | -| hash_flow.rb:744:10:744:17 | ...[...] | hash_flow.rb:736:15:736:25 | call to taint : | hash_flow.rb:744:10:744:17 | ...[...] | $@ | hash_flow.rb:736:15:736:25 | call to taint : | call to taint : | -| hash_flow.rb:745:10:745:17 | ...[...] | hash_flow.rb:738:29:738:39 | call to taint : | hash_flow.rb:745:10:745:17 | ...[...] | $@ | hash_flow.rb:738:29:738:39 | call to taint : | call to taint : | +| hash_flow.rb:73:10:73:19 | ...[...] | hash_flow.rb:72:25:72:34 | call to taint : | hash_flow.rb:73:10:73:19 | ...[...] | $@ | hash_flow.rb:72:25:72:34 | call to taint : | call to taint : | +| hash_flow.rb:77:10:77:19 | ...[...] | hash_flow.rb:76:26:76:35 | call to taint : | hash_flow.rb:77:10:77:19 | ...[...] | $@ | hash_flow.rb:76:26:76:35 | call to taint : | call to taint : | +| hash_flow.rb:85:10:85:18 | ...[...] | hash_flow.rb:84:26:84:35 | call to taint : | hash_flow.rb:85:10:85:18 | ...[...] | $@ | hash_flow.rb:84:26:84:35 | call to taint : | call to taint : | +| hash_flow.rb:97:10:97:18 | ...[...] | hash_flow.rb:93:15:93:24 | call to taint : | hash_flow.rb:97:10:97:18 | ...[...] | $@ | hash_flow.rb:93:15:93:24 | call to taint : | call to taint : | +| hash_flow.rb:106:10:106:10 | b | hash_flow.rb:105:21:105:30 | call to taint : | hash_flow.rb:106:10:106:10 | b | $@ | hash_flow.rb:105:21:105:30 | call to taint : | call to taint : | +| hash_flow.rb:114:10:114:17 | ...[...] | hash_flow.rb:113:24:113:33 | call to taint : | hash_flow.rb:114:10:114:17 | ...[...] | $@ | hash_flow.rb:113:24:113:33 | call to taint : | call to taint : | +| hash_flow.rb:115:10:115:10 | b | hash_flow.rb:113:24:113:33 | call to taint : | hash_flow.rb:115:10:115:10 | b | $@ | hash_flow.rb:113:24:113:33 | call to taint : | call to taint : | +| hash_flow.rb:119:10:119:17 | ...[...] | hash_flow.rb:118:23:118:32 | call to taint : | hash_flow.rb:119:10:119:17 | ...[...] | $@ | hash_flow.rb:118:23:118:32 | call to taint : | call to taint : | +| hash_flow.rb:120:10:120:17 | ...[...] | hash_flow.rb:118:23:118:32 | call to taint : | hash_flow.rb:120:10:120:17 | ...[...] | $@ | hash_flow.rb:118:23:118:32 | call to taint : | call to taint : | +| hash_flow.rb:121:10:121:10 | c | hash_flow.rb:118:23:118:32 | call to taint : | hash_flow.rb:121:10:121:10 | c | $@ | hash_flow.rb:118:23:118:32 | call to taint : | call to taint : | +| hash_flow.rb:132:14:132:25 | key_or_value | hash_flow.rb:128:15:128:24 | call to taint : | hash_flow.rb:132:14:132:25 | key_or_value | $@ | hash_flow.rb:128:15:128:24 | call to taint : | call to taint : | +| hash_flow.rb:136:14:136:18 | value | hash_flow.rb:128:15:128:24 | call to taint : | hash_flow.rb:136:14:136:18 | value | $@ | hash_flow.rb:128:15:128:24 | call to taint : | call to taint : | +| hash_flow.rb:149:10:149:13 | ...[...] | hash_flow.rb:144:15:144:25 | call to taint : | hash_flow.rb:149:10:149:13 | ...[...] | $@ | hash_flow.rb:144:15:144:25 | call to taint : | call to taint : | +| hash_flow.rb:150:10:150:13 | ...[...] | hash_flow.rb:144:15:144:25 | call to taint : | hash_flow.rb:150:10:150:13 | ...[...] | $@ | hash_flow.rb:144:15:144:25 | call to taint : | call to taint : | +| hash_flow.rb:152:10:152:13 | ...[...] | hash_flow.rb:144:15:144:25 | call to taint : | hash_flow.rb:152:10:152:13 | ...[...] | $@ | hash_flow.rb:144:15:144:25 | call to taint : | call to taint : | +| hash_flow.rb:174:10:174:14 | ...[...] | hash_flow.rb:170:15:170:25 | call to taint : | hash_flow.rb:174:10:174:14 | ...[...] | $@ | hash_flow.rb:170:15:170:25 | call to taint : | call to taint : | +| hash_flow.rb:186:10:186:10 | a | hash_flow.rb:182:15:182:25 | call to taint : | hash_flow.rb:186:10:186:10 | a | $@ | hash_flow.rb:182:15:182:25 | call to taint : | call to taint : | +| hash_flow.rb:199:14:199:18 | value | hash_flow.rb:194:15:194:25 | call to taint : | hash_flow.rb:199:14:199:18 | value | $@ | hash_flow.rb:194:15:194:25 | call to taint : | call to taint : | +| hash_flow.rb:201:10:201:14 | ...[...] | hash_flow.rb:194:15:194:25 | call to taint : | hash_flow.rb:201:10:201:14 | ...[...] | $@ | hash_flow.rb:194:15:194:25 | call to taint : | call to taint : | +| hash_flow.rb:202:10:202:17 | ...[...] | hash_flow.rb:194:15:194:25 | call to taint : | hash_flow.rb:202:10:202:17 | ...[...] | $@ | hash_flow.rb:194:15:194:25 | call to taint : | call to taint : | +| hash_flow.rb:217:10:217:21 | call to dig | hash_flow.rb:210:15:210:25 | call to taint : | hash_flow.rb:217:10:217:21 | call to dig | $@ | hash_flow.rb:210:15:210:25 | call to taint : | call to taint : | +| hash_flow.rb:219:10:219:24 | call to dig | hash_flow.rb:213:19:213:29 | call to taint : | hash_flow.rb:219:10:219:24 | call to dig | $@ | hash_flow.rb:213:19:213:29 | call to taint : | call to taint : | +| hash_flow.rb:232:14:232:18 | value | hash_flow.rb:227:15:227:25 | call to taint : | hash_flow.rb:232:14:232:18 | value | $@ | hash_flow.rb:227:15:227:25 | call to taint : | call to taint : | +| hash_flow.rb:234:10:234:14 | ...[...] | hash_flow.rb:227:15:227:25 | call to taint : | hash_flow.rb:234:10:234:14 | ...[...] | $@ | hash_flow.rb:227:15:227:25 | call to taint : | call to taint : | +| hash_flow.rb:248:10:248:14 | ...[...] | hash_flow.rb:242:15:242:25 | call to taint : | hash_flow.rb:248:10:248:14 | ...[...] | $@ | hash_flow.rb:242:15:242:25 | call to taint : | call to taint : | +| hash_flow.rb:261:14:261:18 | value | hash_flow.rb:256:15:256:25 | call to taint : | hash_flow.rb:261:14:261:18 | value | $@ | hash_flow.rb:256:15:256:25 | call to taint : | call to taint : | +| hash_flow.rb:263:10:263:14 | ...[...] | hash_flow.rb:256:15:256:25 | call to taint : | hash_flow.rb:263:10:263:14 | ...[...] | $@ | hash_flow.rb:256:15:256:25 | call to taint : | call to taint : | +| hash_flow.rb:275:14:275:18 | value | hash_flow.rb:271:15:271:25 | call to taint : | hash_flow.rb:275:14:275:18 | value | $@ | hash_flow.rb:271:15:271:25 | call to taint : | call to taint : | +| hash_flow.rb:277:10:277:14 | ...[...] | hash_flow.rb:271:15:271:25 | call to taint : | hash_flow.rb:277:10:277:14 | ...[...] | $@ | hash_flow.rb:271:15:271:25 | call to taint : | call to taint : | +| hash_flow.rb:293:10:293:14 | ...[...] | hash_flow.rb:287:15:287:25 | call to taint : | hash_flow.rb:293:10:293:14 | ...[...] | $@ | hash_flow.rb:287:15:287:25 | call to taint : | call to taint : | +| hash_flow.rb:306:14:306:14 | x | hash_flow.rb:305:20:305:30 | call to taint : | hash_flow.rb:306:14:306:14 | x | $@ | hash_flow.rb:305:20:305:30 | call to taint : | call to taint : | +| hash_flow.rb:308:10:308:10 | b | hash_flow.rb:301:15:301:25 | call to taint : | hash_flow.rb:308:10:308:10 | b | $@ | hash_flow.rb:301:15:301:25 | call to taint : | call to taint : | +| hash_flow.rb:308:10:308:10 | b | hash_flow.rb:303:15:303:25 | call to taint : | hash_flow.rb:308:10:308:10 | b | $@ | hash_flow.rb:303:15:303:25 | call to taint : | call to taint : | +| hash_flow.rb:310:10:310:10 | b | hash_flow.rb:301:15:301:25 | call to taint : | hash_flow.rb:310:10:310:10 | b | $@ | hash_flow.rb:301:15:301:25 | call to taint : | call to taint : | +| hash_flow.rb:312:10:312:10 | b | hash_flow.rb:301:15:301:25 | call to taint : | hash_flow.rb:312:10:312:10 | b | $@ | hash_flow.rb:301:15:301:25 | call to taint : | call to taint : | +| hash_flow.rb:312:10:312:10 | b | hash_flow.rb:311:24:311:34 | call to taint : | hash_flow.rb:312:10:312:10 | b | $@ | hash_flow.rb:311:24:311:34 | call to taint : | call to taint : | +| hash_flow.rb:314:10:314:10 | b | hash_flow.rb:313:24:313:34 | call to taint : | hash_flow.rb:314:10:314:10 | b | $@ | hash_flow.rb:313:24:313:34 | call to taint : | call to taint : | +| hash_flow.rb:316:10:316:10 | b | hash_flow.rb:301:15:301:25 | call to taint : | hash_flow.rb:316:10:316:10 | b | $@ | hash_flow.rb:301:15:301:25 | call to taint : | call to taint : | +| hash_flow.rb:316:10:316:10 | b | hash_flow.rb:303:15:303:25 | call to taint : | hash_flow.rb:316:10:316:10 | b | $@ | hash_flow.rb:303:15:303:25 | call to taint : | call to taint : | +| hash_flow.rb:316:10:316:10 | b | hash_flow.rb:315:23:315:33 | call to taint : | hash_flow.rb:316:10:316:10 | b | $@ | hash_flow.rb:315:23:315:33 | call to taint : | call to taint : | +| hash_flow.rb:328:14:328:14 | x | hash_flow.rb:327:27:327:37 | call to taint : | hash_flow.rb:328:14:328:14 | x | $@ | hash_flow.rb:327:27:327:37 | call to taint : | call to taint : | +| hash_flow.rb:331:10:331:13 | ...[...] | hash_flow.rb:323:15:323:25 | call to taint : | hash_flow.rb:331:10:331:13 | ...[...] | $@ | hash_flow.rb:323:15:323:25 | call to taint : | call to taint : | +| hash_flow.rb:331:10:331:13 | ...[...] | hash_flow.rb:325:15:325:25 | call to taint : | hash_flow.rb:331:10:331:13 | ...[...] | $@ | hash_flow.rb:325:15:325:25 | call to taint : | call to taint : | +| hash_flow.rb:331:10:331:13 | ...[...] | hash_flow.rb:329:9:329:19 | call to taint : | hash_flow.rb:331:10:331:13 | ...[...] | $@ | hash_flow.rb:329:9:329:19 | call to taint : | call to taint : | +| hash_flow.rb:333:10:333:13 | ...[...] | hash_flow.rb:323:15:323:25 | call to taint : | hash_flow.rb:333:10:333:13 | ...[...] | $@ | hash_flow.rb:323:15:323:25 | call to taint : | call to taint : | +| hash_flow.rb:335:10:335:13 | ...[...] | hash_flow.rb:323:15:323:25 | call to taint : | hash_flow.rb:335:10:335:13 | ...[...] | $@ | hash_flow.rb:323:15:323:25 | call to taint : | call to taint : | +| hash_flow.rb:335:10:335:13 | ...[...] | hash_flow.rb:325:15:325:25 | call to taint : | hash_flow.rb:335:10:335:13 | ...[...] | $@ | hash_flow.rb:325:15:325:25 | call to taint : | call to taint : | +| hash_flow.rb:348:14:348:18 | value | hash_flow.rb:342:15:342:25 | call to taint : | hash_flow.rb:348:14:348:18 | value | $@ | hash_flow.rb:342:15:342:25 | call to taint : | call to taint : | +| hash_flow.rb:348:14:348:18 | value | hash_flow.rb:344:15:344:25 | call to taint : | hash_flow.rb:348:14:348:18 | value | $@ | hash_flow.rb:344:15:344:25 | call to taint : | call to taint : | +| hash_flow.rb:351:10:351:16 | ( ... ) | hash_flow.rb:342:15:342:25 | call to taint : | hash_flow.rb:351:10:351:16 | ( ... ) | $@ | hash_flow.rb:342:15:342:25 | call to taint : | call to taint : | +| hash_flow.rb:364:14:364:18 | value | hash_flow.rb:358:15:358:25 | call to taint : | hash_flow.rb:364:14:364:18 | value | $@ | hash_flow.rb:358:15:358:25 | call to taint : | call to taint : | +| hash_flow.rb:364:14:364:18 | value | hash_flow.rb:360:15:360:25 | call to taint : | hash_flow.rb:364:14:364:18 | value | $@ | hash_flow.rb:360:15:360:25 | call to taint : | call to taint : | +| hash_flow.rb:367:10:367:19 | ( ... ) | hash_flow.rb:358:15:358:25 | call to taint : | hash_flow.rb:367:10:367:19 | ( ... ) | $@ | hash_flow.rb:358:15:358:25 | call to taint : | call to taint : | +| hash_flow.rb:379:10:379:15 | ( ... ) | hash_flow.rb:374:15:374:25 | call to taint : | hash_flow.rb:379:10:379:15 | ( ... ) | $@ | hash_flow.rb:374:15:374:25 | call to taint : | call to taint : | +| hash_flow.rb:379:10:379:15 | ( ... ) | hash_flow.rb:376:15:376:25 | call to taint : | hash_flow.rb:379:10:379:15 | ( ... ) | $@ | hash_flow.rb:376:15:376:25 | call to taint : | call to taint : | +| hash_flow.rb:392:14:392:18 | value | hash_flow.rb:386:15:386:25 | call to taint : | hash_flow.rb:392:14:392:18 | value | $@ | hash_flow.rb:386:15:386:25 | call to taint : | call to taint : | +| hash_flow.rb:392:14:392:18 | value | hash_flow.rb:388:15:388:25 | call to taint : | hash_flow.rb:392:14:392:18 | value | $@ | hash_flow.rb:388:15:388:25 | call to taint : | call to taint : | +| hash_flow.rb:395:10:395:19 | ( ... ) | hash_flow.rb:386:15:386:25 | call to taint : | hash_flow.rb:395:10:395:19 | ( ... ) | $@ | hash_flow.rb:386:15:386:25 | call to taint : | call to taint : | +| hash_flow.rb:396:10:396:16 | ( ... ) | hash_flow.rb:386:15:386:25 | call to taint : | hash_flow.rb:396:10:396:16 | ( ... ) | $@ | hash_flow.rb:386:15:386:25 | call to taint : | call to taint : | +| hash_flow.rb:414:14:414:22 | old_value | hash_flow.rb:403:15:403:25 | call to taint : | hash_flow.rb:414:14:414:22 | old_value | $@ | hash_flow.rb:403:15:403:25 | call to taint : | call to taint : | +| hash_flow.rb:414:14:414:22 | old_value | hash_flow.rb:405:15:405:25 | call to taint : | hash_flow.rb:414:14:414:22 | old_value | $@ | hash_flow.rb:405:15:405:25 | call to taint : | call to taint : | +| hash_flow.rb:414:14:414:22 | old_value | hash_flow.rb:408:15:408:25 | call to taint : | hash_flow.rb:414:14:414:22 | old_value | $@ | hash_flow.rb:408:15:408:25 | call to taint : | call to taint : | +| hash_flow.rb:414:14:414:22 | old_value | hash_flow.rb:410:15:410:25 | call to taint : | hash_flow.rb:414:14:414:22 | old_value | $@ | hash_flow.rb:410:15:410:25 | call to taint : | call to taint : | +| hash_flow.rb:415:14:415:22 | new_value | hash_flow.rb:403:15:403:25 | call to taint : | hash_flow.rb:415:14:415:22 | new_value | $@ | hash_flow.rb:403:15:403:25 | call to taint : | call to taint : | +| hash_flow.rb:415:14:415:22 | new_value | hash_flow.rb:405:15:405:25 | call to taint : | hash_flow.rb:415:14:415:22 | new_value | $@ | hash_flow.rb:405:15:405:25 | call to taint : | call to taint : | +| hash_flow.rb:415:14:415:22 | new_value | hash_flow.rb:408:15:408:25 | call to taint : | hash_flow.rb:415:14:415:22 | new_value | $@ | hash_flow.rb:408:15:408:25 | call to taint : | call to taint : | +| hash_flow.rb:415:14:415:22 | new_value | hash_flow.rb:410:15:410:25 | call to taint : | hash_flow.rb:415:14:415:22 | new_value | $@ | hash_flow.rb:410:15:410:25 | call to taint : | call to taint : | +| hash_flow.rb:417:10:417:19 | ( ... ) | hash_flow.rb:403:15:403:25 | call to taint : | hash_flow.rb:417:10:417:19 | ( ... ) | $@ | hash_flow.rb:403:15:403:25 | call to taint : | call to taint : | +| hash_flow.rb:419:10:419:19 | ( ... ) | hash_flow.rb:405:15:405:25 | call to taint : | hash_flow.rb:419:10:419:19 | ( ... ) | $@ | hash_flow.rb:405:15:405:25 | call to taint : | call to taint : | +| hash_flow.rb:420:10:420:19 | ( ... ) | hash_flow.rb:408:15:408:25 | call to taint : | hash_flow.rb:420:10:420:19 | ( ... ) | $@ | hash_flow.rb:408:15:408:25 | call to taint : | call to taint : | +| hash_flow.rb:422:10:422:19 | ( ... ) | hash_flow.rb:410:15:410:25 | call to taint : | hash_flow.rb:422:10:422:19 | ( ... ) | $@ | hash_flow.rb:410:15:410:25 | call to taint : | call to taint : | +| hash_flow.rb:440:14:440:22 | old_value | hash_flow.rb:429:15:429:25 | call to taint : | hash_flow.rb:440:14:440:22 | old_value | $@ | hash_flow.rb:429:15:429:25 | call to taint : | call to taint : | +| hash_flow.rb:440:14:440:22 | old_value | hash_flow.rb:431:15:431:25 | call to taint : | hash_flow.rb:440:14:440:22 | old_value | $@ | hash_flow.rb:431:15:431:25 | call to taint : | call to taint : | +| hash_flow.rb:440:14:440:22 | old_value | hash_flow.rb:434:15:434:25 | call to taint : | hash_flow.rb:440:14:440:22 | old_value | $@ | hash_flow.rb:434:15:434:25 | call to taint : | call to taint : | +| hash_flow.rb:440:14:440:22 | old_value | hash_flow.rb:436:15:436:25 | call to taint : | hash_flow.rb:440:14:440:22 | old_value | $@ | hash_flow.rb:436:15:436:25 | call to taint : | call to taint : | +| hash_flow.rb:441:14:441:22 | new_value | hash_flow.rb:429:15:429:25 | call to taint : | hash_flow.rb:441:14:441:22 | new_value | $@ | hash_flow.rb:429:15:429:25 | call to taint : | call to taint : | +| hash_flow.rb:441:14:441:22 | new_value | hash_flow.rb:431:15:431:25 | call to taint : | hash_flow.rb:441:14:441:22 | new_value | $@ | hash_flow.rb:431:15:431:25 | call to taint : | call to taint : | +| hash_flow.rb:441:14:441:22 | new_value | hash_flow.rb:434:15:434:25 | call to taint : | hash_flow.rb:441:14:441:22 | new_value | $@ | hash_flow.rb:434:15:434:25 | call to taint : | call to taint : | +| hash_flow.rb:441:14:441:22 | new_value | hash_flow.rb:436:15:436:25 | call to taint : | hash_flow.rb:441:14:441:22 | new_value | $@ | hash_flow.rb:436:15:436:25 | call to taint : | call to taint : | +| hash_flow.rb:443:10:443:19 | ( ... ) | hash_flow.rb:429:15:429:25 | call to taint : | hash_flow.rb:443:10:443:19 | ( ... ) | $@ | hash_flow.rb:429:15:429:25 | call to taint : | call to taint : | +| hash_flow.rb:445:10:445:19 | ( ... ) | hash_flow.rb:431:15:431:25 | call to taint : | hash_flow.rb:445:10:445:19 | ( ... ) | $@ | hash_flow.rb:431:15:431:25 | call to taint : | call to taint : | +| hash_flow.rb:446:10:446:19 | ( ... ) | hash_flow.rb:434:15:434:25 | call to taint : | hash_flow.rb:446:10:446:19 | ( ... ) | $@ | hash_flow.rb:434:15:434:25 | call to taint : | call to taint : | +| hash_flow.rb:448:10:448:19 | ( ... ) | hash_flow.rb:436:15:436:25 | call to taint : | hash_flow.rb:448:10:448:19 | ( ... ) | $@ | hash_flow.rb:436:15:436:25 | call to taint : | call to taint : | +| hash_flow.rb:450:10:450:20 | ( ... ) | hash_flow.rb:429:15:429:25 | call to taint : | hash_flow.rb:450:10:450:20 | ( ... ) | $@ | hash_flow.rb:429:15:429:25 | call to taint : | call to taint : | +| hash_flow.rb:452:10:452:20 | ( ... ) | hash_flow.rb:431:15:431:25 | call to taint : | hash_flow.rb:452:10:452:20 | ( ... ) | $@ | hash_flow.rb:431:15:431:25 | call to taint : | call to taint : | +| hash_flow.rb:453:10:453:20 | ( ... ) | hash_flow.rb:434:15:434:25 | call to taint : | hash_flow.rb:453:10:453:20 | ( ... ) | $@ | hash_flow.rb:434:15:434:25 | call to taint : | call to taint : | +| hash_flow.rb:455:10:455:20 | ( ... ) | hash_flow.rb:436:15:436:25 | call to taint : | hash_flow.rb:455:10:455:20 | ( ... ) | $@ | hash_flow.rb:436:15:436:25 | call to taint : | call to taint : | +| hash_flow.rb:467:10:467:13 | ...[...] | hash_flow.rb:462:15:462:25 | call to taint : | hash_flow.rb:467:10:467:13 | ...[...] | $@ | hash_flow.rb:462:15:462:25 | call to taint : | call to taint : | +| hash_flow.rb:479:14:479:18 | value | hash_flow.rb:474:15:474:25 | call to taint : | hash_flow.rb:479:14:479:18 | value | $@ | hash_flow.rb:474:15:474:25 | call to taint : | call to taint : | +| hash_flow.rb:482:10:482:14 | ...[...] | hash_flow.rb:474:15:474:25 | call to taint : | hash_flow.rb:482:10:482:14 | ...[...] | $@ | hash_flow.rb:474:15:474:25 | call to taint : | call to taint : | +| hash_flow.rb:494:14:494:18 | value | hash_flow.rb:489:15:489:25 | call to taint : | hash_flow.rb:494:14:494:18 | value | $@ | hash_flow.rb:489:15:489:25 | call to taint : | call to taint : | +| hash_flow.rb:497:10:497:14 | ...[...] | hash_flow.rb:489:15:489:25 | call to taint : | hash_flow.rb:497:10:497:14 | ...[...] | $@ | hash_flow.rb:489:15:489:25 | call to taint : | call to taint : | +| hash_flow.rb:498:10:498:17 | ...[...] | hash_flow.rb:489:15:489:25 | call to taint : | hash_flow.rb:498:10:498:17 | ...[...] | $@ | hash_flow.rb:489:15:489:25 | call to taint : | call to taint : | +| hash_flow.rb:513:10:513:20 | ( ... ) | hash_flow.rb:505:15:505:25 | call to taint : | hash_flow.rb:513:10:513:20 | ( ... ) | $@ | hash_flow.rb:505:15:505:25 | call to taint : | call to taint : | +| hash_flow.rb:515:10:515:20 | ( ... ) | hash_flow.rb:507:15:507:25 | call to taint : | hash_flow.rb:515:10:515:20 | ( ... ) | $@ | hash_flow.rb:507:15:507:25 | call to taint : | call to taint : | +| hash_flow.rb:526:14:526:18 | value | hash_flow.rb:520:15:520:25 | call to taint : | hash_flow.rb:526:14:526:18 | value | $@ | hash_flow.rb:520:15:520:25 | call to taint : | call to taint : | +| hash_flow.rb:526:14:526:18 | value | hash_flow.rb:522:15:522:25 | call to taint : | hash_flow.rb:526:14:526:18 | value | $@ | hash_flow.rb:522:15:522:25 | call to taint : | call to taint : | +| hash_flow.rb:529:10:529:16 | ( ... ) | hash_flow.rb:520:15:520:25 | call to taint : | hash_flow.rb:529:10:529:16 | ( ... ) | $@ | hash_flow.rb:520:15:520:25 | call to taint : | call to taint : | +| hash_flow.rb:542:14:542:18 | value | hash_flow.rb:536:15:536:25 | call to taint : | hash_flow.rb:542:14:542:18 | value | $@ | hash_flow.rb:536:15:536:25 | call to taint : | call to taint : | +| hash_flow.rb:542:14:542:18 | value | hash_flow.rb:538:15:538:25 | call to taint : | hash_flow.rb:542:14:542:18 | value | $@ | hash_flow.rb:538:15:538:25 | call to taint : | call to taint : | +| hash_flow.rb:545:10:545:19 | ( ... ) | hash_flow.rb:536:15:536:25 | call to taint : | hash_flow.rb:545:10:545:19 | ( ... ) | $@ | hash_flow.rb:536:15:536:25 | call to taint : | call to taint : | +| hash_flow.rb:557:10:557:19 | ( ... ) | hash_flow.rb:552:15:552:25 | call to taint : | hash_flow.rb:557:10:557:19 | ( ... ) | $@ | hash_flow.rb:552:15:552:25 | call to taint : | call to taint : | +| hash_flow.rb:559:10:559:15 | ( ... ) | hash_flow.rb:552:15:552:25 | call to taint : | hash_flow.rb:559:10:559:15 | ( ... ) | $@ | hash_flow.rb:552:15:552:25 | call to taint : | call to taint : | +| hash_flow.rb:559:10:559:15 | ( ... ) | hash_flow.rb:554:15:554:25 | call to taint : | hash_flow.rb:559:10:559:15 | ( ... ) | $@ | hash_flow.rb:554:15:554:25 | call to taint : | call to taint : | +| hash_flow.rb:571:10:571:16 | ( ... ) | hash_flow.rb:566:15:566:25 | call to taint : | hash_flow.rb:571:10:571:16 | ( ... ) | $@ | hash_flow.rb:566:15:566:25 | call to taint : | call to taint : | +| hash_flow.rb:576:10:576:16 | ( ... ) | hash_flow.rb:566:15:566:25 | call to taint : | hash_flow.rb:576:10:576:16 | ( ... ) | $@ | hash_flow.rb:566:15:566:25 | call to taint : | call to taint : | +| hash_flow.rb:578:10:578:16 | ( ... ) | hash_flow.rb:568:15:568:25 | call to taint : | hash_flow.rb:578:10:578:16 | ( ... ) | $@ | hash_flow.rb:568:15:568:25 | call to taint : | call to taint : | +| hash_flow.rb:591:10:591:18 | ( ... ) | hash_flow.rb:585:15:585:25 | call to taint : | hash_flow.rb:591:10:591:18 | ( ... ) | $@ | hash_flow.rb:585:15:585:25 | call to taint : | call to taint : | +| hash_flow.rb:591:10:591:18 | ( ... ) | hash_flow.rb:587:15:587:25 | call to taint : | hash_flow.rb:591:10:591:18 | ( ... ) | $@ | hash_flow.rb:587:15:587:25 | call to taint : | call to taint : | +| hash_flow.rb:603:10:603:16 | ( ... ) | hash_flow.rb:598:15:598:25 | call to taint : | hash_flow.rb:603:10:603:16 | ( ... ) | $@ | hash_flow.rb:598:15:598:25 | call to taint : | call to taint : | +| hash_flow.rb:605:10:605:16 | ( ... ) | hash_flow.rb:600:15:600:25 | call to taint : | hash_flow.rb:605:10:605:16 | ( ... ) | $@ | hash_flow.rb:600:15:600:25 | call to taint : | call to taint : | +| hash_flow.rb:609:14:609:18 | value | hash_flow.rb:598:15:598:25 | call to taint : | hash_flow.rb:609:14:609:18 | value | $@ | hash_flow.rb:598:15:598:25 | call to taint : | call to taint : | +| hash_flow.rb:609:14:609:18 | value | hash_flow.rb:600:15:600:25 | call to taint : | hash_flow.rb:609:14:609:18 | value | $@ | hash_flow.rb:600:15:600:25 | call to taint : | call to taint : | +| hash_flow.rb:612:10:612:16 | ( ... ) | hash_flow.rb:610:14:610:24 | call to taint : | hash_flow.rb:612:10:612:16 | ( ... ) | $@ | hash_flow.rb:610:14:610:24 | call to taint : | call to taint : | +| hash_flow.rb:624:10:624:17 | ( ... ) | hash_flow.rb:619:15:619:25 | call to taint : | hash_flow.rb:624:10:624:17 | ( ... ) | $@ | hash_flow.rb:619:15:619:25 | call to taint : | call to taint : | +| hash_flow.rb:624:10:624:17 | ( ... ) | hash_flow.rb:621:15:621:25 | call to taint : | hash_flow.rb:624:10:624:17 | ( ... ) | $@ | hash_flow.rb:621:15:621:25 | call to taint : | call to taint : | +| hash_flow.rb:625:10:625:17 | ( ... ) | hash_flow.rb:619:15:619:25 | call to taint : | hash_flow.rb:625:10:625:17 | ( ... ) | $@ | hash_flow.rb:619:15:619:25 | call to taint : | call to taint : | +| hash_flow.rb:625:10:625:17 | ( ... ) | hash_flow.rb:621:15:621:25 | call to taint : | hash_flow.rb:625:10:625:17 | ( ... ) | $@ | hash_flow.rb:621:15:621:25 | call to taint : | call to taint : | +| hash_flow.rb:626:10:626:17 | ( ... ) | hash_flow.rb:619:15:619:25 | call to taint : | hash_flow.rb:626:10:626:17 | ( ... ) | $@ | hash_flow.rb:619:15:619:25 | call to taint : | call to taint : | +| hash_flow.rb:626:10:626:17 | ( ... ) | hash_flow.rb:621:15:621:25 | call to taint : | hash_flow.rb:626:10:626:17 | ( ... ) | $@ | hash_flow.rb:621:15:621:25 | call to taint : | call to taint : | +| hash_flow.rb:638:10:638:20 | ( ... ) | hash_flow.rb:633:15:633:25 | call to taint : | hash_flow.rb:638:10:638:20 | ( ... ) | $@ | hash_flow.rb:633:15:633:25 | call to taint : | call to taint : | +| hash_flow.rb:638:10:638:20 | ( ... ) | hash_flow.rb:635:15:635:25 | call to taint : | hash_flow.rb:638:10:638:20 | ( ... ) | $@ | hash_flow.rb:635:15:635:25 | call to taint : | call to taint : | +| hash_flow.rb:639:10:639:20 | ( ... ) | hash_flow.rb:633:15:633:25 | call to taint : | hash_flow.rb:639:10:639:20 | ( ... ) | $@ | hash_flow.rb:633:15:633:25 | call to taint : | call to taint : | +| hash_flow.rb:639:10:639:20 | ( ... ) | hash_flow.rb:635:15:635:25 | call to taint : | hash_flow.rb:639:10:639:20 | ( ... ) | $@ | hash_flow.rb:635:15:635:25 | call to taint : | call to taint : | +| hash_flow.rb:640:10:640:20 | ( ... ) | hash_flow.rb:633:15:633:25 | call to taint : | hash_flow.rb:640:10:640:20 | ( ... ) | $@ | hash_flow.rb:633:15:633:25 | call to taint : | call to taint : | +| hash_flow.rb:640:10:640:20 | ( ... ) | hash_flow.rb:635:15:635:25 | call to taint : | hash_flow.rb:640:10:640:20 | ( ... ) | $@ | hash_flow.rb:635:15:635:25 | call to taint : | call to taint : | +| hash_flow.rb:652:14:652:18 | value | hash_flow.rb:647:15:647:25 | call to taint : | hash_flow.rb:652:14:652:18 | value | $@ | hash_flow.rb:647:15:647:25 | call to taint : | call to taint : | +| hash_flow.rb:652:14:652:18 | value | hash_flow.rb:649:15:649:25 | call to taint : | hash_flow.rb:652:14:652:18 | value | $@ | hash_flow.rb:649:15:649:25 | call to taint : | call to taint : | +| hash_flow.rb:655:10:655:19 | ( ... ) | hash_flow.rb:647:15:647:25 | call to taint : | hash_flow.rb:655:10:655:19 | ( ... ) | $@ | hash_flow.rb:647:15:647:25 | call to taint : | call to taint : | +| hash_flow.rb:656:10:656:16 | ( ... ) | hash_flow.rb:653:9:653:19 | call to taint : | hash_flow.rb:656:10:656:16 | ( ... ) | $@ | hash_flow.rb:653:9:653:19 | call to taint : | call to taint : | +| hash_flow.rb:668:14:668:18 | value | hash_flow.rb:663:15:663:25 | call to taint : | hash_flow.rb:668:14:668:18 | value | $@ | hash_flow.rb:663:15:663:25 | call to taint : | call to taint : | +| hash_flow.rb:668:14:668:18 | value | hash_flow.rb:665:15:665:25 | call to taint : | hash_flow.rb:668:14:668:18 | value | $@ | hash_flow.rb:665:15:665:25 | call to taint : | call to taint : | +| hash_flow.rb:671:10:671:19 | ( ... ) | hash_flow.rb:669:9:669:19 | call to taint : | hash_flow.rb:671:10:671:19 | ( ... ) | $@ | hash_flow.rb:669:9:669:19 | call to taint : | call to taint : | +| hash_flow.rb:689:14:689:22 | old_value | hash_flow.rb:678:15:678:25 | call to taint : | hash_flow.rb:689:14:689:22 | old_value | $@ | hash_flow.rb:678:15:678:25 | call to taint : | call to taint : | +| hash_flow.rb:689:14:689:22 | old_value | hash_flow.rb:680:15:680:25 | call to taint : | hash_flow.rb:689:14:689:22 | old_value | $@ | hash_flow.rb:680:15:680:25 | call to taint : | call to taint : | +| hash_flow.rb:689:14:689:22 | old_value | hash_flow.rb:683:15:683:25 | call to taint : | hash_flow.rb:689:14:689:22 | old_value | $@ | hash_flow.rb:683:15:683:25 | call to taint : | call to taint : | +| hash_flow.rb:689:14:689:22 | old_value | hash_flow.rb:685:15:685:25 | call to taint : | hash_flow.rb:689:14:689:22 | old_value | $@ | hash_flow.rb:685:15:685:25 | call to taint : | call to taint : | +| hash_flow.rb:690:14:690:22 | new_value | hash_flow.rb:678:15:678:25 | call to taint : | hash_flow.rb:690:14:690:22 | new_value | $@ | hash_flow.rb:678:15:678:25 | call to taint : | call to taint : | +| hash_flow.rb:690:14:690:22 | new_value | hash_flow.rb:680:15:680:25 | call to taint : | hash_flow.rb:690:14:690:22 | new_value | $@ | hash_flow.rb:680:15:680:25 | call to taint : | call to taint : | +| hash_flow.rb:690:14:690:22 | new_value | hash_flow.rb:683:15:683:25 | call to taint : | hash_flow.rb:690:14:690:22 | new_value | $@ | hash_flow.rb:683:15:683:25 | call to taint : | call to taint : | +| hash_flow.rb:690:14:690:22 | new_value | hash_flow.rb:685:15:685:25 | call to taint : | hash_flow.rb:690:14:690:22 | new_value | $@ | hash_flow.rb:685:15:685:25 | call to taint : | call to taint : | +| hash_flow.rb:692:10:692:19 | ( ... ) | hash_flow.rb:678:15:678:25 | call to taint : | hash_flow.rb:692:10:692:19 | ( ... ) | $@ | hash_flow.rb:678:15:678:25 | call to taint : | call to taint : | +| hash_flow.rb:694:10:694:19 | ( ... ) | hash_flow.rb:680:15:680:25 | call to taint : | hash_flow.rb:694:10:694:19 | ( ... ) | $@ | hash_flow.rb:680:15:680:25 | call to taint : | call to taint : | +| hash_flow.rb:695:10:695:19 | ( ... ) | hash_flow.rb:683:15:683:25 | call to taint : | hash_flow.rb:695:10:695:19 | ( ... ) | $@ | hash_flow.rb:683:15:683:25 | call to taint : | call to taint : | +| hash_flow.rb:697:10:697:19 | ( ... ) | hash_flow.rb:685:15:685:25 | call to taint : | hash_flow.rb:697:10:697:19 | ( ... ) | $@ | hash_flow.rb:685:15:685:25 | call to taint : | call to taint : | +| hash_flow.rb:699:10:699:20 | ( ... ) | hash_flow.rb:678:15:678:25 | call to taint : | hash_flow.rb:699:10:699:20 | ( ... ) | $@ | hash_flow.rb:678:15:678:25 | call to taint : | call to taint : | +| hash_flow.rb:701:10:701:20 | ( ... ) | hash_flow.rb:680:15:680:25 | call to taint : | hash_flow.rb:701:10:701:20 | ( ... ) | $@ | hash_flow.rb:680:15:680:25 | call to taint : | call to taint : | +| hash_flow.rb:702:10:702:20 | ( ... ) | hash_flow.rb:683:15:683:25 | call to taint : | hash_flow.rb:702:10:702:20 | ( ... ) | $@ | hash_flow.rb:683:15:683:25 | call to taint : | call to taint : | +| hash_flow.rb:704:10:704:20 | ( ... ) | hash_flow.rb:685:15:685:25 | call to taint : | hash_flow.rb:704:10:704:20 | ( ... ) | $@ | hash_flow.rb:685:15:685:25 | call to taint : | call to taint : | +| hash_flow.rb:716:10:716:15 | ( ... ) | hash_flow.rb:711:15:711:25 | call to taint : | hash_flow.rb:716:10:716:15 | ( ... ) | $@ | hash_flow.rb:711:15:711:25 | call to taint : | call to taint : | +| hash_flow.rb:716:10:716:15 | ( ... ) | hash_flow.rb:713:15:713:25 | call to taint : | hash_flow.rb:716:10:716:15 | ( ... ) | $@ | hash_flow.rb:713:15:713:25 | call to taint : | call to taint : | +| hash_flow.rb:728:10:728:13 | ...[...] | hash_flow.rb:723:15:723:25 | call to taint : | hash_flow.rb:728:10:728:13 | ...[...] | $@ | hash_flow.rb:723:15:723:25 | call to taint : | call to taint : | +| hash_flow.rb:730:10:730:13 | ...[...] | hash_flow.rb:723:15:723:25 | call to taint : | hash_flow.rb:730:10:730:13 | ...[...] | $@ | hash_flow.rb:723:15:723:25 | call to taint : | call to taint : | +| hash_flow.rb:730:10:730:13 | ...[...] | hash_flow.rb:725:15:725:25 | call to taint : | hash_flow.rb:730:10:730:13 | ...[...] | $@ | hash_flow.rb:725:15:725:25 | call to taint : | call to taint : | +| hash_flow.rb:747:10:747:17 | ...[...] | hash_flow.rb:737:15:737:25 | call to taint : | hash_flow.rb:747:10:747:17 | ...[...] | $@ | hash_flow.rb:737:15:737:25 | call to taint : | call to taint : | +| hash_flow.rb:749:10:749:17 | ...[...] | hash_flow.rb:739:15:739:25 | call to taint : | hash_flow.rb:749:10:749:17 | ...[...] | $@ | hash_flow.rb:739:15:739:25 | call to taint : | call to taint : | +| hash_flow.rb:750:10:750:17 | ...[...] | hash_flow.rb:742:15:742:25 | call to taint : | hash_flow.rb:750:10:750:17 | ...[...] | $@ | hash_flow.rb:742:15:742:25 | call to taint : | call to taint : | +| hash_flow.rb:752:10:752:17 | ...[...] | hash_flow.rb:744:15:744:25 | call to taint : | hash_flow.rb:752:10:752:17 | ...[...] | $@ | hash_flow.rb:744:15:744:25 | call to taint : | call to taint : | +| hash_flow.rb:753:10:753:17 | ...[...] | hash_flow.rb:746:29:746:39 | call to taint : | hash_flow.rb:753:10:753:17 | ...[...] | $@ | hash_flow.rb:746:29:746:39 | call to taint : | call to taint : | diff --git a/ruby/ql/test/library-tests/dataflow/hash-flow/hash_flow.rb b/ruby/ql/test/library-tests/dataflow/hash-flow/hash_flow.rb index f9de83dd47c..6802475b89b 100644 --- a/ruby/ql/test/library-tests/dataflow/hash-flow/hash_flow.rb +++ b/ruby/ql/test/library-tests/dataflow/hash-flow/hash_flow.rb @@ -68,6 +68,14 @@ def m3() hash4 = Hash[:a, taint(3.4), :b, 1] sink(hash4[:a]) # $ hasValueFlow=3.4 sink(hash4[:b]) + + hash5 = Hash["a" => taint(3.5), "b" => 1] + sink(hash5["a"]) # $ hasValueFlow=3.5 + sink(hash5["b"]) + + hash6 = Hash[{"a" => taint(3.6), "b" => 1}] + sink(hash6["a"]) # $ hasValueFlow=3.6 + sink(hash6["b"]) end m3() From 06fecf3869ec2739f946ad69250147c320ffe18e Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Tue, 9 Aug 2022 15:12:31 +0100 Subject: [PATCH 503/505] Swift: Include 'any!' in the the CFG tree for 'any' expressions. --- .../swift/controlflow/internal/ControlFlowGraphImpl.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll b/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll index b367aa80d52..2b7e7b5b963 100644 --- a/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll +++ b/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll @@ -1676,8 +1676,8 @@ module Exprs { } } - private class TryTree extends AstStandardPostOrderTree { - override TryExpr ast; + private class AnyTryTree extends AstStandardPostOrderTree { + override AnyTryExpr ast; override ControlFlowElement getChildElement(int i) { i = 0 and From 5ee11c3d7b4009e224169a5ab6dbb33d8b997df3 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Tue, 9 Aug 2022 15:12:42 +0100 Subject: [PATCH 504/505] Swift: Accept test changes. --- .../controlflow/graph/Cfg.expected | 82 +++++++++++++++++++ .../CWE-079/UnsafeWebViewFetch.expected | 12 +++ .../Security/CWE-079/UnsafeWebViewFetch.swift | 6 +- 3 files changed, 97 insertions(+), 3 deletions(-) diff --git a/swift/ql/test/library-tests/controlflow/graph/Cfg.expected b/swift/ql/test/library-tests/controlflow/graph/Cfg.expected index e7ea8fc83b6..c6e8d620e85 100644 --- a/swift/ql/test/library-tests/controlflow/graph/Cfg.expected +++ b/swift/ql/test/library-tests/controlflow/graph/Cfg.expected @@ -200,6 +200,7 @@ cfg.swift: #-----| -> Did not throw. # 29| call to print(_:separator:terminator:) +#-----| -> mightThrow(x:) # 29| default separator #-----| -> default terminator @@ -219,6 +220,43 @@ cfg.swift: # 29| [...] #-----| -> [...] +# 30| try! ... +#-----| -> print(_:separator:terminator:) + +# 30| mightThrow(x:) +#-----| -> 0 + +# 30| call to mightThrow(x:) +#-----| -> try! ... +#-----| exception -> case ... + +# 30| 0 +#-----| -> call to mightThrow(x:) + +# 31| print(_:separator:terminator:) +#-----| -> Still did not throw. + +# 31| call to print(_:separator:terminator:) +#-----| -> 0 + +# 31| default separator +#-----| -> default terminator + +# 31| default terminator +#-----| -> call to print(_:separator:terminator:) + +# 31| (Any) ... +#-----| -> [...] + +# 31| Still did not throw. +#-----| -> (Any) ... + +# 31| [...] +#-----| -> default separator + +# 31| [...] +#-----| -> [...] + # 33| case ... #-----| -> ... is ... @@ -5306,6 +5344,7 @@ cfg.swift: #-----| -> Did not throw. # 386| call to print(_:separator:terminator:) +#-----| -> mightThrow(x:) # 386| default separator #-----| -> default terminator @@ -5325,6 +5364,49 @@ cfg.swift: # 386| [...] #-----| -> [...] +# 387| try! ... +#-----| -> print(_:separator:terminator:) + +# 387| mightThrow(x:) +#-----| -> 0 + +# 387| call to mightThrow(x:) +#-----| exception -> exit doWithoutCatch(x:) (normal) +#-----| -> try! ... + +# 387| 0 +#-----| -> call to mightThrow(x:) + +# 388| print(_:separator:terminator:) +#-----| -> Still did not throw. + +# 388| call to print(_:separator:terminator:) +#-----| -> 0 + +# 388| default separator +#-----| -> default terminator + +# 388| default terminator +#-----| -> call to print(_:separator:terminator:) + +# 388| (Any) ... +#-----| -> [...] + +# 388| Still did not throw. +#-----| -> (Any) ... + +# 388| [...] +#-----| -> default separator + +# 388| [...] +#-----| -> [...] + +# 390| return ... +#-----| return -> exit doWithoutCatch(x:) (normal) + +# 390| 0 +#-----| -> return ... + # 394| (unnamed function decl) # 394| enter (unnamed function decl) diff --git a/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.expected b/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.expected index 53983de94c5..f11e0bbe6f2 100644 --- a/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.expected +++ b/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.expected @@ -5,6 +5,9 @@ edges | UnsafeWebViewFetch.swift:94:10:94:37 | try ... : | UnsafeWebViewFetch.swift:167:25:167:39 | call to getRemoteData() | | UnsafeWebViewFetch.swift:94:10:94:37 | try ... : | UnsafeWebViewFetch.swift:206:17:206:31 | call to getRemoteData() : | | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:94:10:94:37 | try ... : | +| UnsafeWebViewFetch.swift:103:30:103:84 | call to ... : | UnsafeWebViewFetch.swift:103:25:103:84 | try! ... | +| UnsafeWebViewFetch.swift:105:18:105:72 | call to ... : | UnsafeWebViewFetch.swift:106:25:106:25 | data | +| UnsafeWebViewFetch.swift:109:30:109:53 | call to ... : | UnsafeWebViewFetch.swift:109:25:109:53 | try! ... | | UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:121:25:121:25 | remoteString | | UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:124:25:124:51 | ... call to +(_:_:) ... | | UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:127:25:127:25 | "..." | @@ -36,6 +39,12 @@ edges nodes | UnsafeWebViewFetch.swift:94:10:94:37 | try ... : | semmle.label | try ... : | | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | semmle.label | call to ... : | +| UnsafeWebViewFetch.swift:103:25:103:84 | try! ... | semmle.label | try! ... | +| UnsafeWebViewFetch.swift:103:30:103:84 | call to ... : | semmle.label | call to ... : | +| UnsafeWebViewFetch.swift:105:18:105:72 | call to ... : | semmle.label | call to ... : | +| UnsafeWebViewFetch.swift:106:25:106:25 | data | semmle.label | data | +| UnsafeWebViewFetch.swift:109:25:109:53 | try! ... | semmle.label | try! ... | +| UnsafeWebViewFetch.swift:109:30:109:53 | call to ... : | semmle.label | call to ... : | | UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | semmle.label | call to getRemoteData() : | | UnsafeWebViewFetch.swift:120:25:120:39 | call to getRemoteData() | semmle.label | call to getRemoteData() | | UnsafeWebViewFetch.swift:121:25:121:25 | remoteString | semmle.label | remoteString | @@ -71,6 +80,9 @@ nodes | UnsafeWebViewFetch.swift:211:25:211:25 | htmlData | semmle.label | htmlData | subpaths #select +| UnsafeWebViewFetch.swift:103:25:103:84 | try! ... | UnsafeWebViewFetch.swift:103:30:103:84 | call to ... : | UnsafeWebViewFetch.swift:103:25:103:84 | try! ... | Tainted data is used in a WebView fetch without restricting the base URL. | +| UnsafeWebViewFetch.swift:106:25:106:25 | data | UnsafeWebViewFetch.swift:105:18:105:72 | call to ... : | UnsafeWebViewFetch.swift:106:25:106:25 | data | Tainted data is used in a WebView fetch without restricting the base URL. | +| UnsafeWebViewFetch.swift:109:25:109:53 | try! ... | UnsafeWebViewFetch.swift:109:30:109:53 | call to ... : | UnsafeWebViewFetch.swift:109:25:109:53 | try! ... | Tainted data is used in a WebView fetch without restricting the base URL. | | UnsafeWebViewFetch.swift:120:25:120:39 | call to getRemoteData() | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:120:25:120:39 | call to getRemoteData() | Tainted data is used in a WebView fetch without restricting the base URL. | | UnsafeWebViewFetch.swift:121:25:121:25 | remoteString | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:121:25:121:25 | remoteString | Tainted data is used in a WebView fetch without restricting the base URL. | | UnsafeWebViewFetch.swift:124:25:124:51 | ... call to +(_:_:) ... | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:124:25:124:51 | ... call to +(_:_:) ... | Tainted data is used in a WebView fetch without restricting the base URL. | diff --git a/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.swift b/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.swift index c6fc1628fbe..6ee075b4d3d 100644 --- a/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.swift +++ b/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.swift @@ -100,13 +100,13 @@ func getRemoteData() -> String { func testSimpleFlows() { let webview = UIWebView() - webview.loadHTMLString(try! String(contentsOf: URL(string: "http://example.com/")!), baseURL: nil) // BAD [NOT DETECTED] + webview.loadHTMLString(try! String(contentsOf: URL(string: "http://example.com/")!), baseURL: nil) // BAD let data = try! String(contentsOf: URL(string: "http://example.com/")!) - webview.loadHTMLString(data, baseURL: nil) // BAD [NOT DETECTED] + webview.loadHTMLString(data, baseURL: nil) // BAD let url = URL(string: "http://example.com/") - webview.loadHTMLString(try! String(contentsOf: url!), baseURL: nil) // BAD [NOT DETECTED] + webview.loadHTMLString(try! String(contentsOf: url!), baseURL: nil) // BAD } func testUIWebView() { From cb19ae2638027fdc51cc792fd1983da355aa2014 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 10 Aug 2022 00:16:31 +0000 Subject: [PATCH 505/505] Add changed framework coverage reports --- .../library-coverage/coverage.csv | 55 ++++++++++--------- .../library-coverage/coverage.rst | 6 +- 2 files changed, 31 insertions(+), 30 deletions(-) diff --git a/csharp/documentation/library-coverage/coverage.csv b/csharp/documentation/library-coverage/coverage.csv index 2bc6ca863ac..485242620f3 100644 --- a/csharp/documentation/library-coverage/coverage.csv +++ b/csharp/documentation/library-coverage/coverage.csv @@ -1,27 +1,28 @@ -package,sink,source,summary,sink:code,sink:html,sink:remote,sink:sql,sink:xss,source:local,summary:taint,summary:value -Dapper,55,,,,,,55,,,, -JsonToItemsTaskFactory,,,7,,,,,,,7, -Microsoft.ApplicationBlocks.Data,28,,,,,,28,,,, -Microsoft.CSharp,,,24,,,,,,,24, -Microsoft.EntityFrameworkCore,6,,,,,,6,,,, -Microsoft.Extensions.Caching.Distributed,,,15,,,,,,,15, -Microsoft.Extensions.Caching.Memory,,,46,,,,,,,45,1 -Microsoft.Extensions.Configuration,,,83,,,,,,,80,3 -Microsoft.Extensions.DependencyInjection,,,62,,,,,,,62, -Microsoft.Extensions.DependencyModel,,,12,,,,,,,12, -Microsoft.Extensions.FileProviders,,,15,,,,,,,15, -Microsoft.Extensions.FileSystemGlobbing,,,15,,,,,,,13,2 -Microsoft.Extensions.Hosting,,,17,,,,,,,16,1 -Microsoft.Extensions.Http,,,10,,,,,,,10, -Microsoft.Extensions.Logging,,,37,,,,,,,37, -Microsoft.Extensions.Options,,,8,,,,,,,8, -Microsoft.Extensions.Primitives,,,63,,,,,,,63, -Microsoft.Interop,,,27,,,,,,,27, -Microsoft.NET.Build.Tasks,,,1,,,,,,,1, -Microsoft.NETCore.Platforms.BuildTasks,,,4,,,,,,,4, -Microsoft.VisualBasic,,,9,,,,,,,5,4 -Microsoft.Win32,,,8,,,,,,,8, -MySql.Data.MySqlClient,48,,,,,,48,,,, -Newtonsoft.Json,,,91,,,,,,,73,18 -ServiceStack,194,,7,27,,75,92,,,7, -System,28,3,12038,,4,,23,1,3,10096,1942 +package,sink,source,summary,sink:code,sink:encryption-decryptor,sink:encryption-encryptor,sink:encryption-keyprop,sink:encryption-symmetrickey,sink:html,sink:remote,sink:sql,sink:xss,source:local,summary:taint,summary:value +Dapper,55,,,,,,,,,,55,,,, +JsonToItemsTaskFactory,,,7,,,,,,,,,,,7, +Microsoft.ApplicationBlocks.Data,28,,,,,,,,,,28,,,, +Microsoft.CSharp,,,24,,,,,,,,,,,24, +Microsoft.EntityFrameworkCore,6,,,,,,,,,,6,,,, +Microsoft.Extensions.Caching.Distributed,,,15,,,,,,,,,,,15, +Microsoft.Extensions.Caching.Memory,,,46,,,,,,,,,,,45,1 +Microsoft.Extensions.Configuration,,,83,,,,,,,,,,,80,3 +Microsoft.Extensions.DependencyInjection,,,62,,,,,,,,,,,62, +Microsoft.Extensions.DependencyModel,,,12,,,,,,,,,,,12, +Microsoft.Extensions.FileProviders,,,15,,,,,,,,,,,15, +Microsoft.Extensions.FileSystemGlobbing,,,15,,,,,,,,,,,13,2 +Microsoft.Extensions.Hosting,,,17,,,,,,,,,,,16,1 +Microsoft.Extensions.Http,,,10,,,,,,,,,,,10, +Microsoft.Extensions.Logging,,,37,,,,,,,,,,,37, +Microsoft.Extensions.Options,,,8,,,,,,,,,,,8, +Microsoft.Extensions.Primitives,,,63,,,,,,,,,,,63, +Microsoft.Interop,,,27,,,,,,,,,,,27, +Microsoft.NET.Build.Tasks,,,1,,,,,,,,,,,1, +Microsoft.NETCore.Platforms.BuildTasks,,,4,,,,,,,,,,,4, +Microsoft.VisualBasic,,,9,,,,,,,,,,,5,4 +Microsoft.Win32,,,8,,,,,,,,,,,8, +MySql.Data.MySqlClient,48,,,,,,,,,,48,,,, +Newtonsoft.Json,,,91,,,,,,,,,,,73,18 +ServiceStack,194,,7,27,,,,,,75,92,,,7, +System,35,3,11796,,1,1,1,,4,,25,3,3,9854,1942 +Windows.Security.Cryptography.Core,1,,,,,,,1,,,,,,, diff --git a/csharp/documentation/library-coverage/coverage.rst b/csharp/documentation/library-coverage/coverage.rst index 076d2078d4b..d088c041e8b 100644 --- a/csharp/documentation/library-coverage/coverage.rst +++ b/csharp/documentation/library-coverage/coverage.rst @@ -8,7 +8,7 @@ C# framework & library support Framework / library,Package,Flow sources,Taint & value steps,Sinks (total),`CWE-079` :sub:`Cross-site scripting` `ServiceStack `_,"``ServiceStack.*``, ``ServiceStack``",,7,194, - System,"``System.*``, ``System``",3,12038,28,5 - Others,"``Dapper``, ``JsonToItemsTaskFactory``, ``Microsoft.ApplicationBlocks.Data``, ``Microsoft.CSharp``, ``Microsoft.EntityFrameworkCore``, ``Microsoft.Extensions.Caching.Distributed``, ``Microsoft.Extensions.Caching.Memory``, ``Microsoft.Extensions.Configuration``, ``Microsoft.Extensions.DependencyInjection``, ``Microsoft.Extensions.DependencyModel``, ``Microsoft.Extensions.FileProviders``, ``Microsoft.Extensions.FileSystemGlobbing``, ``Microsoft.Extensions.Hosting``, ``Microsoft.Extensions.Http``, ``Microsoft.Extensions.Logging``, ``Microsoft.Extensions.Options``, ``Microsoft.Extensions.Primitives``, ``Microsoft.Interop``, ``Microsoft.NET.Build.Tasks``, ``Microsoft.NETCore.Platforms.BuildTasks``, ``Microsoft.VisualBasic``, ``Microsoft.Win32``, ``MySql.Data.MySqlClient``, ``Newtonsoft.Json``",,554,137, - Totals,,3,12599,359,5 + System,"``System.*``, ``System``",3,11796,35,7 + Others,"``Dapper``, ``JsonToItemsTaskFactory``, ``Microsoft.ApplicationBlocks.Data``, ``Microsoft.CSharp``, ``Microsoft.EntityFrameworkCore``, ``Microsoft.Extensions.Caching.Distributed``, ``Microsoft.Extensions.Caching.Memory``, ``Microsoft.Extensions.Configuration``, ``Microsoft.Extensions.DependencyInjection``, ``Microsoft.Extensions.DependencyModel``, ``Microsoft.Extensions.FileProviders``, ``Microsoft.Extensions.FileSystemGlobbing``, ``Microsoft.Extensions.Hosting``, ``Microsoft.Extensions.Http``, ``Microsoft.Extensions.Logging``, ``Microsoft.Extensions.Options``, ``Microsoft.Extensions.Primitives``, ``Microsoft.Interop``, ``Microsoft.NET.Build.Tasks``, ``Microsoft.NETCore.Platforms.BuildTasks``, ``Microsoft.VisualBasic``, ``Microsoft.Win32``, ``MySql.Data.MySqlClient``, ``Newtonsoft.Json``, ``Windows.Security.Cryptography.Core``",,554,138, + Totals,,3,12357,367,7